Imported Upstream version 2.72.3
[platform/upstream/glib.git] / gio / tests / file.c
1 #include <locale.h>
2 #include <string.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <gio/gio.h>
6 #include <gio/gfiledescriptorbased.h>
7 #ifdef G_OS_UNIX
8 #include <sys/stat.h>
9 #endif
10
11 static void
12 test_basic_for_file (GFile       *file,
13                      const gchar *suffix)
14 {
15   gchar *s;
16
17   s = g_file_get_basename (file);
18   g_assert_cmpstr (s, ==, "testfile");
19   g_free (s);
20
21   s = g_file_get_uri (file);
22   g_assert_true (g_str_has_prefix (s, "file://"));
23   g_assert_true (g_str_has_suffix (s, suffix));
24   g_free (s);
25
26   g_assert_true (g_file_has_uri_scheme (file, "file"));
27   s = g_file_get_uri_scheme (file);
28   g_assert_cmpstr (s, ==, "file");
29   g_free (s);
30 }
31
32 static void
33 test_basic (void)
34 {
35   GFile *file;
36
37   file = g_file_new_for_path ("./some/directory/testfile");
38   test_basic_for_file (file, "/some/directory/testfile");
39   g_object_unref (file);
40 }
41
42 static void
43 test_build_filename (void)
44 {
45   GFile *file;
46
47   file = g_file_new_build_filename (".", "some", "directory", "testfile", NULL);
48   test_basic_for_file (file, "/some/directory/testfile");
49   g_object_unref (file);
50
51   file = g_file_new_build_filename ("testfile", NULL);
52   test_basic_for_file (file, "/testfile");
53   g_object_unref (file);
54 }
55
56 static void
57 test_parent (void)
58 {
59   GFile *file;
60   GFile *file2;
61   GFile *parent;
62   GFile *root;
63
64   file = g_file_new_for_path ("./some/directory/testfile");
65   file2 = g_file_new_for_path ("./some/directory");
66   root = g_file_new_for_path ("/");
67
68   g_assert_true (g_file_has_parent (file, file2));
69
70   parent = g_file_get_parent (file);
71   g_assert_true (g_file_equal (parent, file2));
72   g_object_unref (parent);
73
74   g_assert_null (g_file_get_parent (root));
75
76   g_object_unref (file);
77   g_object_unref (file2);
78   g_object_unref (root);
79 }
80
81 static void
82 test_child (void)
83 {
84   GFile *file;
85   GFile *child;
86   GFile *child2;
87
88   file = g_file_new_for_path ("./some/directory");
89   child = g_file_get_child (file, "child");
90   g_assert_true (g_file_has_parent (child, file));
91
92   child2 = g_file_get_child_for_display_name (file, "child2", NULL);
93   g_assert_true (g_file_has_parent (child2, file));
94
95   g_object_unref (child);
96   g_object_unref (child2);
97   g_object_unref (file);
98 }
99
100 static void
101 test_empty_path (void)
102 {
103   GFile *file = NULL;
104
105   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2328");
106   g_test_summary ("Check that creating a file with an empty path results in errors");
107
108   /* Creating the file must always succeed. */
109   file = g_file_new_for_path ("");
110   g_assert_nonnull (file);
111
112   /* But then querying its path should indicate it’s invalid. */
113   g_assert_null (g_file_get_path (file));
114   g_assert_null (g_file_get_basename (file));
115   g_assert_null (g_file_get_parent (file));
116
117   g_object_unref (file);
118 }
119
120 static void
121 test_type (void)
122 {
123   GFile *datapath_f;
124   GFile *file;
125   GFileType type;
126   GError *error = NULL;
127
128   datapath_f = g_file_new_for_path (g_test_get_dir (G_TEST_DIST));
129
130   file = g_file_get_child (datapath_f, "g-icon.c");
131   type = g_file_query_file_type (file, 0, NULL);
132   g_assert_cmpint (type, ==, G_FILE_TYPE_REGULAR);
133   g_object_unref (file);
134
135   file = g_file_get_child (datapath_f, "cert-tests");
136   type = g_file_query_file_type (file, 0, NULL);
137   g_assert_cmpint (type, ==, G_FILE_TYPE_DIRECTORY);
138
139   g_file_read (file, NULL, &error);
140   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY);
141   g_error_free (error);
142   g_object_unref (file);
143
144   g_object_unref (datapath_f);
145 }
146
147 static void
148 test_parse_name (void)
149 {
150   GFile *file;
151   gchar *name;
152
153   file = g_file_new_for_uri ("file://somewhere");
154   name = g_file_get_parse_name (file);
155   g_assert_cmpstr (name, ==, "file://somewhere");
156   g_object_unref (file);
157   g_free (name);
158
159   file = g_file_parse_name ("~foo");
160   name = g_file_get_parse_name (file);
161   g_assert_nonnull (name);
162   g_object_unref (file);
163   g_free (name);
164 }
165
166 typedef struct
167 {
168   GMainContext *context;
169   GFile *file;
170   GFileMonitor *monitor;
171   GOutputStream *ostream;
172   GInputStream *istream;
173   gint buffersize;
174   gint monitor_created;
175   gint monitor_deleted;
176   gint monitor_changed;
177   gchar *monitor_path;
178   gsize pos;
179   const gchar *data;
180   gchar *buffer;
181   guint timeout;
182   gboolean file_deleted;
183   gboolean timed_out;
184 } CreateDeleteData;
185
186 static void
187 monitor_changed (GFileMonitor      *monitor,
188                  GFile             *file,
189                  GFile             *other_file,
190                  GFileMonitorEvent  event_type,
191                  gpointer           user_data)
192 {
193   CreateDeleteData *data = user_data;
194   gchar *path;
195   const gchar *peeked_path;
196
197   path = g_file_get_path (file);
198   peeked_path = g_file_peek_path (file);
199   g_assert_cmpstr (data->monitor_path, ==, path);
200   g_assert_cmpstr (path, ==, peeked_path);
201   g_free (path);
202
203   if (event_type == G_FILE_MONITOR_EVENT_CREATED)
204     data->monitor_created++;
205   if (event_type == G_FILE_MONITOR_EVENT_DELETED)
206     data->monitor_deleted++;
207   if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
208     data->monitor_changed++;
209
210   g_main_context_wakeup (data->context);
211 }
212
213 static void
214 iclosed_cb (GObject      *source,
215             GAsyncResult *res,
216             gpointer      user_data)
217 {
218   CreateDeleteData *data = user_data;
219   GError *error;
220   gboolean ret;
221
222   error = NULL;
223   ret = g_input_stream_close_finish (data->istream, res, &error);
224   g_assert_no_error (error);
225   g_assert_true (ret);
226
227   g_assert_true (g_input_stream_is_closed (data->istream));
228
229   ret = g_file_delete (data->file, NULL, &error);
230   g_assert_true (ret);
231   g_assert_no_error (error);
232
233   data->file_deleted = TRUE;
234   g_main_context_wakeup (data->context);
235 }
236
237 static void
238 read_cb (GObject      *source,
239          GAsyncResult *res,
240          gpointer      user_data)
241 {
242   CreateDeleteData *data = user_data;
243   GError *error;
244   gssize size;
245
246   error = NULL;
247   size = g_input_stream_read_finish (data->istream, res, &error);
248   g_assert_no_error (error);
249
250   data->pos += size;
251   if (data->pos < strlen (data->data))
252     {
253       g_input_stream_read_async (data->istream,
254                                  data->buffer + data->pos,
255                                  strlen (data->data) - data->pos,
256                                  0,
257                                  NULL,
258                                  read_cb,
259                                  data);
260     }
261   else
262     {
263       g_assert_cmpstr (data->buffer, ==, data->data);
264       g_assert_false (g_input_stream_is_closed (data->istream));
265       g_input_stream_close_async (data->istream, 0, NULL, iclosed_cb, data);
266     }
267 }
268
269 static void
270 ipending_cb (GObject      *source,
271              GAsyncResult *res,
272              gpointer      user_data)
273 {
274   CreateDeleteData *data = user_data;
275   GError *error;
276
277   error = NULL;
278   g_input_stream_read_finish (data->istream, res, &error);
279   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
280   g_error_free (error);
281 }
282
283 static void
284 skipped_cb (GObject      *source,
285             GAsyncResult *res,
286             gpointer      user_data)
287 {
288   CreateDeleteData *data = user_data;
289   GError *error;
290   gssize size;
291
292   error = NULL;
293   size = g_input_stream_skip_finish (data->istream, res, &error);
294   g_assert_no_error (error);
295   g_assert_cmpint (size, ==, data->pos);
296
297   g_input_stream_read_async (data->istream,
298                              data->buffer + data->pos,
299                              strlen (data->data) - data->pos,
300                              0,
301                              NULL,
302                              read_cb,
303                              data);
304   /* check that we get a pending error */
305   g_input_stream_read_async (data->istream,
306                              data->buffer + data->pos,
307                              strlen (data->data) - data->pos,
308                              0,
309                              NULL,
310                              ipending_cb,
311                              data);
312 }
313
314 static void
315 opened_cb (GObject      *source,
316            GAsyncResult *res,
317            gpointer      user_data)
318 {
319   GFileInputStream *base;
320   CreateDeleteData *data = user_data;
321   GError *error;
322
323   error = NULL;
324   base = g_file_read_finish (data->file, res, &error);
325   g_assert_no_error (error);
326
327   if (data->buffersize == 0)
328     data->istream = G_INPUT_STREAM (g_object_ref (base));
329   else
330     data->istream = g_buffered_input_stream_new_sized (G_INPUT_STREAM (base), data->buffersize);
331   g_object_unref (base);
332
333   data->buffer = g_new0 (gchar, strlen (data->data) + 1);
334
335   /* copy initial segment directly, then skip */
336   memcpy (data->buffer, data->data, 10);
337   data->pos = 10;
338
339   g_input_stream_skip_async (data->istream,
340                              10,
341                              0,
342                              NULL,
343                              skipped_cb,
344                              data);
345 }
346
347 static void
348 oclosed_cb (GObject      *source,
349             GAsyncResult *res,
350             gpointer      user_data)
351 {
352   CreateDeleteData *data = user_data;
353   GError *error;
354   gboolean ret;
355
356   error = NULL;
357   ret = g_output_stream_close_finish (data->ostream, res, &error);
358   g_assert_no_error (error);
359   g_assert_true (ret);
360   g_assert_true (g_output_stream_is_closed (data->ostream));
361
362   g_file_read_async (data->file, 0, NULL, opened_cb, data);
363 }
364
365 static void
366 written_cb (GObject      *source,
367             GAsyncResult *res,
368             gpointer      user_data)
369 {
370   CreateDeleteData *data = user_data;
371   gssize size;
372   GError *error;
373
374   error = NULL;
375   size = g_output_stream_write_finish (data->ostream, res, &error);
376   g_assert_no_error (error);
377
378   data->pos += size;
379   if (data->pos < strlen (data->data))
380     {
381       g_output_stream_write_async (data->ostream,
382                                    data->data + data->pos,
383                                    strlen (data->data) - data->pos,
384                                    0,
385                                    NULL,
386                                    written_cb,
387                                    data);
388     }
389   else
390     {
391       g_assert_false (g_output_stream_is_closed (data->ostream));
392       g_output_stream_close_async (data->ostream, 0, NULL, oclosed_cb, data);
393     }
394 }
395
396 static void
397 opending_cb (GObject      *source,
398              GAsyncResult *res,
399              gpointer      user_data)
400 {
401   CreateDeleteData *data = user_data;
402   GError *error;
403
404   error = NULL;
405   g_output_stream_write_finish (data->ostream, res, &error);
406   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
407   g_error_free (error);
408 }
409
410 static void
411 created_cb (GObject      *source,
412             GAsyncResult *res,
413             gpointer      user_data)
414 {
415   GFileOutputStream *base;
416   CreateDeleteData *data = user_data;
417   GError *error;
418
419   error = NULL;
420   base = g_file_create_finish (G_FILE (source), res, &error);
421   g_assert_no_error (error);
422   g_assert_true (g_file_query_exists (data->file, NULL));
423
424   if (data->buffersize == 0)
425     data->ostream = G_OUTPUT_STREAM (g_object_ref (base));
426   else
427     data->ostream = g_buffered_output_stream_new_sized (G_OUTPUT_STREAM (base), data->buffersize);
428   g_object_unref (base);
429
430   g_output_stream_write_async (data->ostream,
431                                data->data,
432                                strlen (data->data),
433                                0,
434                                NULL,
435                                written_cb,
436                                data);
437   /* check that we get a pending error */
438   g_output_stream_write_async (data->ostream,
439                                data->data,
440                                strlen (data->data),
441                                0,
442                                NULL,
443                                opending_cb,
444                                data);
445 }
446
447 static gboolean
448 stop_timeout (gpointer user_data)
449 {
450   CreateDeleteData *data = user_data;
451
452   data->timed_out = TRUE;
453   g_main_context_wakeup (data->context);
454
455   return G_SOURCE_REMOVE;
456 }
457
458 /*
459  * This test does a fully async create-write-read-delete.
460  * Callbackistan.
461  */
462 static void
463 test_create_delete (gconstpointer d)
464 {
465   GError *error;
466   CreateDeleteData *data;
467   GFileIOStream *iostream;
468
469   data = g_new0 (CreateDeleteData, 1);
470
471   data->buffersize = GPOINTER_TO_INT (d);
472   data->data = "abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVXYZ0123456789";
473   data->pos = 0;
474
475   data->file = g_file_new_tmp ("g_file_create_delete_XXXXXX",
476                                &iostream, NULL);
477   g_assert_nonnull (data->file);
478   g_object_unref (iostream);
479
480   data->monitor_path = g_file_get_path (data->file);
481   remove (data->monitor_path);
482
483   g_assert_false (g_file_query_exists (data->file, NULL));
484
485   error = NULL;
486   data->monitor = g_file_monitor_file (data->file, 0, NULL, &error);
487   g_assert_no_error (error);
488
489   /* This test doesn't work with GPollFileMonitor, because it assumes
490    * that the monitor will notice a create immediately followed by a
491    * delete, rather than coalescing them into nothing.
492    */
493   /* This test also doesn't work with GKqueueFileMonitor because of
494    * the same reason. Kqueue is able to return a kevent when a file is
495    * created or deleted in a directory. However, the kernel doesn't tell
496    * the program file names, so GKqueueFileMonitor has to calculate the
497    * difference itself. This is usually too slow for rapid file creation
498    * and deletion tests.
499    */
500   if (strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GPollFileMonitor") == 0 ||
501       strcmp (G_OBJECT_TYPE_NAME (data->monitor), "GKqueueFileMonitor") == 0)
502     {
503       g_test_skip ("skipping test for this GFileMonitor implementation");
504       goto skip;
505     }
506
507   g_file_monitor_set_rate_limit (data->monitor, 100);
508
509   g_signal_connect (data->monitor, "changed", G_CALLBACK (monitor_changed), data);
510
511   /* Use the global default main context */
512   data->context = NULL;
513   data->timeout = g_timeout_add_seconds (10, stop_timeout, data);
514
515   g_file_create_async (data->file, 0, 0, NULL, created_cb, data);
516
517   while (!data->timed_out &&
518          (data->monitor_created == 0 ||
519           data->monitor_deleted == 0 ||
520           data->monitor_changed == 0 ||
521           !data->file_deleted))
522     g_main_context_iteration (data->context, TRUE);
523
524   g_source_remove (data->timeout);
525
526   g_assert_false (data->timed_out);
527   g_assert_true (data->file_deleted);
528   g_assert_cmpint (data->monitor_created, ==, 1);
529   g_assert_cmpint (data->monitor_deleted, ==, 1);
530   g_assert_cmpint (data->monitor_changed, >, 0);
531
532   g_assert_false (g_file_monitor_is_cancelled (data->monitor));
533   g_file_monitor_cancel (data->monitor);
534   g_assert_true (g_file_monitor_is_cancelled (data->monitor));
535
536   g_clear_pointer (&data->context, g_main_context_unref);
537   g_object_unref (data->ostream);
538   g_object_unref (data->istream);
539
540  skip:
541   g_object_unref (data->monitor);
542   g_object_unref (data->file);
543   g_free (data->monitor_path);
544   g_free (data->buffer);
545   g_free (data);
546 }
547
548 static const gchar *original_data =
549     "/**\n"
550     " * g_file_replace_contents_async:\n"
551     "**/\n";
552
553 static const gchar *replace_data =
554     "/**\n"
555     " * g_file_replace_contents_async:\n"
556     " * @file: input #GFile.\n"
557     " * @contents: string of contents to replace the file with.\n"
558     " * @length: the length of @contents in bytes.\n"
559     " * @etag: (nullable): a new <link linkend=\"gfile-etag\">entity tag</link> for the @file, or %NULL\n"
560     " * @make_backup: %TRUE if a backup should be created.\n"
561     " * @flags: a set of #GFileCreateFlags.\n"
562     " * @cancellable: optional #GCancellable object, %NULL to ignore.\n"
563     " * @callback: a #GAsyncReadyCallback to call when the request is satisfied\n"
564     " * @user_data: the data to pass to callback function\n"
565     " * \n"
566     " * Starts an asynchronous replacement of @file with the given \n"
567     " * @contents of @length bytes. @etag will replace the document's\n"
568     " * current entity tag.\n"
569     " * \n"
570     " * When this operation has completed, @callback will be called with\n"
571     " * @user_user data, and the operation can be finalized with \n"
572     " * g_file_replace_contents_finish().\n"
573     " * \n"
574     " * If @cancellable is not %NULL, then the operation can be cancelled by\n"
575     " * triggering the cancellable object from another thread. If the operation\n"
576     " * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. \n"
577     " * \n"
578     " * If @make_backup is %TRUE, this function will attempt to \n"
579     " * make a backup of @file.\n"
580     " **/\n";
581
582 typedef struct
583 {
584   GFile *file;
585   const gchar *data;
586   GMainLoop *loop;
587   gboolean again;
588 } ReplaceLoadData;
589
590 static void replaced_cb (GObject      *source,
591                          GAsyncResult *res,
592                          gpointer      user_data);
593
594 static void
595 loaded_cb (GObject      *source,
596            GAsyncResult *res,
597            gpointer      user_data)
598 {
599   ReplaceLoadData *data = user_data;
600   gboolean ret;
601   GError *error;
602   gchar *contents;
603   gsize length;
604
605   error = NULL;
606   ret = g_file_load_contents_finish (data->file, res, &contents, &length, NULL, &error);
607   g_assert_true (ret);
608   g_assert_no_error (error);
609   g_assert_cmpint (length, ==, strlen (data->data));
610   g_assert_cmpstr (contents, ==, data->data);
611
612   g_free (contents);
613
614   if (data->again)
615     {
616       data->again = FALSE;
617       data->data = "pi pa po";
618
619       g_file_replace_contents_async (data->file,
620                                      data->data,
621                                      strlen (data->data),
622                                      NULL,
623                                      FALSE,
624                                      0,
625                                      NULL,
626                                      replaced_cb,
627                                      data);
628     }
629   else
630     {
631        error = NULL;
632        ret = g_file_delete (data->file, NULL, &error);
633        g_assert_no_error (error);
634        g_assert_true (ret);
635        g_assert_false (g_file_query_exists (data->file, NULL));
636
637        g_main_loop_quit (data->loop);
638     }
639 }
640
641 static void
642 replaced_cb (GObject      *source,
643              GAsyncResult *res,
644              gpointer      user_data)
645 {
646   ReplaceLoadData *data = user_data;
647   GError *error;
648
649   error = NULL;
650   g_file_replace_contents_finish (data->file, res, NULL, &error);
651   g_assert_no_error (error);
652
653   g_file_load_contents_async (data->file, NULL, loaded_cb, data);
654 }
655
656 static void
657 test_replace_load (void)
658 {
659   ReplaceLoadData *data;
660   const gchar *path;
661   GFileIOStream *iostream;
662
663   data = g_new0 (ReplaceLoadData, 1);
664   data->again = TRUE;
665   data->data = replace_data;
666
667   data->file = g_file_new_tmp ("g_file_replace_load_XXXXXX",
668                                &iostream, NULL);
669   g_assert_nonnull (data->file);
670   g_object_unref (iostream);
671
672   path = g_file_peek_path (data->file);
673   remove (path);
674
675   g_assert_false (g_file_query_exists (data->file, NULL));
676
677   data->loop = g_main_loop_new (NULL, FALSE);
678
679   g_file_replace_contents_async (data->file,
680                                  data->data,
681                                  strlen (data->data),
682                                  NULL,
683                                  FALSE,
684                                  0,
685                                  NULL,
686                                  replaced_cb,
687                                  data);
688
689   g_main_loop_run (data->loop);
690
691   g_main_loop_unref (data->loop);
692   g_object_unref (data->file);
693   g_free (data);
694 }
695
696 static void
697 test_replace_cancel (void)
698 {
699   GFile *tmpdir, *file;
700   GFileOutputStream *ostream;
701   GFileEnumerator *fenum;
702   GFileInfo *info;
703   GCancellable *cancellable;
704   gchar *path;
705   gchar *contents;
706   gsize nwrote, length;
707   guint count;
708   GError *error = NULL;
709
710   g_test_bug ("https://bugzilla.gnome.org/629301");
711
712   path = g_dir_make_tmp ("g_file_replace_cancel_XXXXXX", &error);
713   g_assert_no_error (error);
714   tmpdir = g_file_new_for_path (path);
715   g_free (path);
716
717   file = g_file_get_child (tmpdir, "file");
718   g_file_replace_contents (file,
719                            original_data,
720                            strlen (original_data),
721                            NULL, FALSE, 0, NULL,
722                            NULL, &error);
723   g_assert_no_error (error);
724
725   ostream = g_file_replace (file, NULL, TRUE, 0, NULL, &error);
726   g_assert_no_error (error);
727
728   g_output_stream_write_all (G_OUTPUT_STREAM (ostream),
729                              replace_data, strlen (replace_data),
730                              &nwrote, NULL, &error);
731   g_assert_no_error (error);
732   g_assert_cmpint (nwrote, ==, strlen (replace_data));
733
734   /* At this point there should be two files; the original and the
735    * temporary.
736    */
737   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
738   g_assert_no_error (error);
739
740   info = g_file_enumerator_next_file (fenum, NULL, &error);
741   g_assert_no_error (error);
742   g_assert_nonnull (info);
743   g_object_unref (info);
744   info = g_file_enumerator_next_file (fenum, NULL, &error);
745   g_assert_no_error (error);
746   g_assert_nonnull (info);
747   g_object_unref (info);
748
749   g_file_enumerator_close (fenum, NULL, &error);
750   g_assert_no_error (error);
751   g_object_unref (fenum);
752
753   /* Also test the g_file_enumerator_iterate() API */
754   fenum = g_file_enumerate_children (tmpdir, NULL, 0, NULL, &error);
755   g_assert_no_error (error);
756   count = 0;
757
758   while (TRUE)
759     {
760       gboolean ret = g_file_enumerator_iterate (fenum, &info, NULL, NULL, &error);
761       g_assert_true (ret);
762       g_assert_no_error (error);
763       if (!info)
764         break;
765       count++;
766     }
767   g_assert_cmpint (count, ==, 2);
768
769   g_file_enumerator_close (fenum, NULL, &error);
770   g_assert_no_error (error);
771   g_object_unref (fenum);
772
773   /* Now test just getting child from the g_file_enumerator_iterate() API */
774   fenum = g_file_enumerate_children (tmpdir, "standard::name", 0, NULL, &error);
775   g_assert_no_error (error);
776   count = 0;
777
778   while (TRUE)
779     {
780       GFile *child;
781       gboolean ret = g_file_enumerator_iterate (fenum, NULL, &child, NULL, &error);
782
783       g_assert_true (ret);
784       g_assert_no_error (error);
785
786       if (!child)
787         break;
788
789       g_assert_true (G_IS_FILE (child));
790       count++;
791     }
792   g_assert_cmpint (count, ==, 2);
793
794   g_file_enumerator_close (fenum, NULL, &error);
795   g_assert_no_error (error);
796   g_object_unref (fenum);
797
798   /* Make sure the temporary gets deleted even if we cancel. */
799   cancellable = g_cancellable_new ();
800   g_cancellable_cancel (cancellable);
801   g_output_stream_close (G_OUTPUT_STREAM (ostream), cancellable, &error);
802   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
803   g_clear_error (&error);
804
805   g_object_unref (cancellable);
806   g_object_unref (ostream);
807
808   /* Make sure that file contents wasn't actually replaced. */
809   g_file_load_contents (file,
810                         NULL,
811                         &contents,
812                         &length,
813                         NULL,
814                         &error);
815   g_assert_no_error (error);
816   g_assert_cmpstr (contents, ==, original_data);
817   g_free (contents);
818
819   g_file_delete (file, NULL, &error);
820   g_assert_no_error (error);
821   g_object_unref (file);
822
823   /* This will only succeed if the temp file was deleted. */
824   g_file_delete (tmpdir, NULL, &error);
825   g_assert_no_error (error);
826   g_object_unref (tmpdir);
827 }
828
829 static void
830 test_replace_symlink (void)
831 {
832 #ifdef G_OS_UNIX
833   gchar *tmpdir_path = NULL;
834   GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
835   GFileOutputStream *stream = NULL;
836   const gchar *new_contents = "this is a test message which should be written to source and not target";
837   gsize n_written;
838   GFileEnumerator *enumerator = NULL;
839   GFileInfo *info = NULL;
840   gchar *contents = NULL;
841   gsize length = 0;
842   GError *local_error = NULL;
843
844   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2325");
845   g_test_summary ("Test that G_FILE_CREATE_REPLACE_DESTINATION doesn’t follow symlinks");
846
847   /* Create a fresh, empty working directory. */
848   tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_XXXXXX", &local_error);
849   g_assert_no_error (local_error);
850   tmpdir = g_file_new_for_path (tmpdir_path);
851
852   g_test_message ("Using temporary directory %s", tmpdir_path);
853   g_free (tmpdir_path);
854
855   /* Create symlink `source` which points to `target`. */
856   source_file = g_file_get_child (tmpdir, "source");
857   target_file = g_file_get_child (tmpdir, "target");
858   g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
859   g_assert_no_error (local_error);
860
861   /* Ensure that `target` doesn’t exist */
862   g_assert_false (g_file_query_exists (target_file, NULL));
863
864   /* Replace the `source` symlink with a regular file using
865    * %G_FILE_CREATE_REPLACE_DESTINATION, which should replace it *without*
866    * following the symlink */
867   stream = g_file_replace (source_file, NULL, FALSE  /* no backup */,
868                            G_FILE_CREATE_REPLACE_DESTINATION, NULL, &local_error);
869   g_assert_no_error (local_error);
870
871   g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
872                              &n_written, NULL, &local_error);
873   g_assert_no_error (local_error);
874   g_assert_cmpint (n_written, ==, strlen (new_contents));
875
876   g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
877   g_assert_no_error (local_error);
878
879   g_clear_object (&stream);
880
881   /* At this point, there should still only be one file: `source`. It should
882    * now be a regular file. `target` should not exist. */
883   enumerator = g_file_enumerate_children (tmpdir,
884                                           G_FILE_ATTRIBUTE_STANDARD_NAME ","
885                                           G_FILE_ATTRIBUTE_STANDARD_TYPE,
886                                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
887   g_assert_no_error (local_error);
888
889   info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
890   g_assert_no_error (local_error);
891   g_assert_nonnull (info);
892
893   g_assert_cmpstr (g_file_info_get_name (info), ==, "source");
894   g_assert_cmpint (g_file_info_get_file_type (info), ==, G_FILE_TYPE_REGULAR);
895
896   g_clear_object (&info);
897
898   info = g_file_enumerator_next_file (enumerator, NULL, &local_error);
899   g_assert_no_error (local_error);
900   g_assert_null (info);
901
902   g_file_enumerator_close (enumerator, NULL, &local_error);
903   g_assert_no_error (local_error);
904   g_clear_object (&enumerator);
905
906   /* Double-check that `target` doesn’t exist */
907   g_assert_false (g_file_query_exists (target_file, NULL));
908
909   /* Check the content of `source`. */
910   g_file_load_contents (source_file,
911                         NULL,
912                         &contents,
913                         &length,
914                         NULL,
915                         &local_error);
916   g_assert_no_error (local_error);
917   g_assert_cmpstr (contents, ==, new_contents);
918   g_assert_cmpuint (length, ==, strlen (new_contents));
919   g_free (contents);
920
921   /* Tidy up. */
922   g_file_delete (source_file, NULL, &local_error);
923   g_assert_no_error (local_error);
924
925   g_file_delete (tmpdir, NULL, &local_error);
926   g_assert_no_error (local_error);
927
928   g_clear_object (&target_file);
929   g_clear_object (&source_file);
930   g_clear_object (&tmpdir);
931 #else  /* if !G_OS_UNIX */
932   g_test_skip ("Symlink replacement tests can only be run on Unix")
933 #endif
934 }
935
936 static void
937 test_replace_symlink_using_etag (void)
938 {
939 #ifdef G_OS_UNIX
940   gchar *tmpdir_path = NULL;
941   GFile *tmpdir = NULL, *source_file = NULL, *target_file = NULL;
942   GFileOutputStream *stream = NULL;
943   const gchar *old_contents = "this is a test message which should be written to target and then overwritten";
944   gchar *old_etag = NULL;
945   const gchar *new_contents = "this is an updated message";
946   gsize n_written;
947   gchar *contents = NULL;
948   gsize length = 0;
949   GFileInfo *info = NULL;
950   GError *local_error = NULL;
951
952   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2417");
953   g_test_summary ("Test that ETag checks work when replacing a file through a symlink");
954
955   /* Create a fresh, empty working directory. */
956   tmpdir_path = g_dir_make_tmp ("g_file_replace_symlink_using_etag_XXXXXX", &local_error);
957   g_assert_no_error (local_error);
958   tmpdir = g_file_new_for_path (tmpdir_path);
959
960   g_test_message ("Using temporary directory %s", tmpdir_path);
961   g_free (tmpdir_path);
962
963   /* Create symlink `source` which points to `target`. */
964   source_file = g_file_get_child (tmpdir, "source");
965   target_file = g_file_get_child (tmpdir, "target");
966   g_file_make_symbolic_link (source_file, "target", NULL, &local_error);
967   g_assert_no_error (local_error);
968
969   /* Sleep for at least 1s to ensure the mtimes of `source` and `target` differ,
970    * as that’s what _g_local_file_info_create_etag() uses to create the ETag,
971    * and one failure mode we’re testing for is that the ETags of `source` and
972    * `target` are conflated. */
973   sleep (1);
974
975   /* Create `target` with some arbitrary content. */
976   stream = g_file_create (target_file, G_FILE_CREATE_NONE, NULL, &local_error);
977   g_assert_no_error (local_error);
978   g_output_stream_write_all (G_OUTPUT_STREAM (stream), old_contents, strlen (old_contents),
979                              &n_written, NULL, &local_error);
980   g_assert_no_error (local_error);
981   g_assert_cmpint (n_written, ==, strlen (old_contents));
982
983   g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
984   g_assert_no_error (local_error);
985
986   old_etag = g_file_output_stream_get_etag (stream);
987   g_assert_nonnull (old_etag);
988   g_assert_cmpstr (old_etag, !=, "");
989
990   g_clear_object (&stream);
991
992   /* Sleep again to ensure the ETag changes again. */
993   sleep (1);
994
995   /* Write out a new copy of the `target`, checking its ETag first. This should
996    * replace `target` by following the symlink. */
997   stream = g_file_replace (source_file, old_etag, FALSE  /* no backup */,
998                            G_FILE_CREATE_NONE, NULL, &local_error);
999   g_assert_no_error (local_error);
1000
1001   g_output_stream_write_all (G_OUTPUT_STREAM (stream), new_contents, strlen (new_contents),
1002                              &n_written, NULL, &local_error);
1003   g_assert_no_error (local_error);
1004   g_assert_cmpint (n_written, ==, strlen (new_contents));
1005
1006   g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &local_error);
1007   g_assert_no_error (local_error);
1008
1009   g_clear_object (&stream);
1010
1011   /* At this point, there should be a regular file, `target`, containing
1012    * @new_contents; and a symlink `source` which points to `target`. */
1013   g_assert_cmpint (g_file_query_file_type (source_file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL), ==, G_FILE_TYPE_SYMBOLIC_LINK);
1014   g_assert_cmpint (g_file_query_file_type (target_file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL), ==, G_FILE_TYPE_REGULAR);
1015
1016   /* Check the content of `target`. */
1017   g_file_load_contents (target_file,
1018                         NULL,
1019                         &contents,
1020                         &length,
1021                         NULL,
1022                         &local_error);
1023   g_assert_no_error (local_error);
1024   g_assert_cmpstr (contents, ==, new_contents);
1025   g_assert_cmpuint (length, ==, strlen (new_contents));
1026   g_free (contents);
1027
1028   /* And check its ETag value has changed. */
1029   info = g_file_query_info (target_file, G_FILE_ATTRIBUTE_ETAG_VALUE,
1030                             G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1031   g_assert_no_error (local_error);
1032   g_assert_cmpstr (g_file_info_get_etag (info), !=, old_etag);
1033
1034   g_clear_object (&info);
1035   g_free (old_etag);
1036
1037   /* Tidy up. */
1038   g_file_delete (target_file, NULL, &local_error);
1039   g_assert_no_error (local_error);
1040
1041   g_file_delete (source_file, NULL, &local_error);
1042   g_assert_no_error (local_error);
1043
1044   g_file_delete (tmpdir, NULL, &local_error);
1045   g_assert_no_error (local_error);
1046
1047   g_clear_object (&target_file);
1048   g_clear_object (&source_file);
1049   g_clear_object (&tmpdir);
1050 #else  /* if !G_OS_UNIX */
1051   g_test_skip ("Symlink replacement tests can only be run on Unix")
1052 #endif
1053 }
1054
1055 /* FIXME: These tests have only been checked on Linux. Most of them are probably
1056  * applicable on Windows, too, but that has not been tested yet.
1057  * See https://gitlab.gnome.org/GNOME/glib/-/issues/2325 */
1058 #ifdef __linux__
1059
1060 /* Different kinds of file which create_test_file() can create. */
1061 typedef enum
1062 {
1063   FILE_TEST_SETUP_TYPE_NONEXISTENT,
1064   FILE_TEST_SETUP_TYPE_REGULAR_EMPTY,
1065   FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY,
1066   FILE_TEST_SETUP_TYPE_DIRECTORY,
1067   FILE_TEST_SETUP_TYPE_SOCKET,
1068   FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING,
1069   FILE_TEST_SETUP_TYPE_SYMLINK_VALID,
1070 } FileTestSetupType;
1071
1072 /* Create file `tmpdir/basename`, of type @setup_type, and chmod it to
1073  * @setup_mode. Return the #GFile representing it. Abort on any errors. */
1074 static GFile *
1075 create_test_file (GFile             *tmpdir,
1076                   const gchar       *basename,
1077                   FileTestSetupType  setup_type,
1078                   guint              setup_mode)
1079 {
1080   GFile *test_file = g_file_get_child (tmpdir, basename);
1081   gchar *target_basename = g_strdup_printf ("%s-target", basename);  /* for symlinks */
1082   GFile *target_file = g_file_get_child (tmpdir, target_basename);
1083   GError *local_error = NULL;
1084
1085   switch (setup_type)
1086     {
1087     case FILE_TEST_SETUP_TYPE_NONEXISTENT:
1088       /* Nothing to do here. */
1089       g_assert (setup_mode == 0);
1090       break;
1091     case FILE_TEST_SETUP_TYPE_REGULAR_EMPTY:
1092     case FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY:
1093         {
1094           gchar *contents = NULL;
1095
1096           if (setup_type == FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY)
1097             contents = g_strdup_printf ("this is some test content in %s", basename);
1098           else
1099             contents = g_strdup ("");
1100
1101           g_file_set_contents (g_file_peek_path (test_file), contents, -1, &local_error);
1102           g_assert_no_error (local_error);
1103
1104           g_file_set_attribute_uint32 (test_file, G_FILE_ATTRIBUTE_UNIX_MODE,
1105                                        setup_mode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
1106                                        NULL, &local_error);
1107           g_assert_no_error (local_error);
1108
1109           g_free (contents);
1110           break;
1111         }
1112     case FILE_TEST_SETUP_TYPE_DIRECTORY:
1113       g_assert (setup_mode == 0);
1114
1115       g_file_make_directory (test_file, NULL, &local_error);
1116       g_assert_no_error (local_error);
1117       break;
1118     case FILE_TEST_SETUP_TYPE_SOCKET:
1119       g_assert_no_errno (mknod (g_file_peek_path (test_file), S_IFSOCK | setup_mode, 0));
1120       break;
1121     case FILE_TEST_SETUP_TYPE_SYMLINK_VALID:
1122       g_file_set_contents (g_file_peek_path (target_file), "target file", -1, &local_error);
1123       g_assert_no_error (local_error);
1124       G_GNUC_FALLTHROUGH;
1125     case FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING:
1126       /* Permissions on a symlink are not used by the kernel, so are only
1127        * applicable if the symlink is valid (and are applied to the target) */
1128       g_assert (setup_type != FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING || setup_mode == 0);
1129
1130       g_file_make_symbolic_link (test_file, target_basename, NULL, &local_error);
1131       g_assert_no_error (local_error);
1132
1133       if (setup_type == FILE_TEST_SETUP_TYPE_SYMLINK_VALID)
1134         {
1135           g_file_set_attribute_uint32 (test_file, G_FILE_ATTRIBUTE_UNIX_MODE,
1136                                        setup_mode, G_FILE_QUERY_INFO_NONE,
1137                                        NULL, &local_error);
1138           g_assert_no_error (local_error);
1139         }
1140
1141       if (setup_type == FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING)
1142         {
1143           /* Ensure that the target doesn’t exist */
1144           g_assert_false (g_file_query_exists (target_file, NULL));
1145         }
1146       break;
1147     default:
1148       g_assert_not_reached ();
1149     }
1150
1151   g_clear_object (&target_file);
1152   g_free (target_basename);
1153
1154   return g_steal_pointer (&test_file);
1155 }
1156
1157 /* Check that @test_file is of the @expected_type, has the @expected_mode, and
1158  * (if it’s a regular file) has the @expected_contents or (if it’s a symlink)
1159  * has the symlink target given by @expected_contents.
1160  *
1161  * @test_file must point to the file `tmpdir/basename`.
1162  *
1163  * Aborts on any errors or mismatches against the expectations.
1164  */
1165 static void
1166 check_test_file (GFile             *test_file,
1167                  GFile             *tmpdir,
1168                  const gchar       *basename,
1169                  FileTestSetupType  expected_type,
1170                  guint              expected_mode,
1171                  const gchar       *expected_contents)
1172 {
1173   gchar *target_basename = g_strdup_printf ("%s-target", basename);  /* for symlinks */
1174   GFile *target_file = g_file_get_child (tmpdir, target_basename);
1175   GFileType test_file_type = g_file_query_file_type (test_file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
1176   GFileInfo *info = NULL;
1177   GError *local_error = NULL;
1178
1179   switch (expected_type)
1180     {
1181     case FILE_TEST_SETUP_TYPE_NONEXISTENT:
1182       g_assert (expected_mode == 0);
1183       g_assert (expected_contents == NULL);
1184
1185       g_assert_false (g_file_query_exists (test_file, NULL));
1186       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_UNKNOWN);
1187       break;
1188     case FILE_TEST_SETUP_TYPE_REGULAR_EMPTY:
1189     case FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY:
1190       g_assert (expected_type != FILE_TEST_SETUP_TYPE_REGULAR_EMPTY || expected_contents == NULL);
1191       g_assert (expected_type != FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY || expected_contents != NULL);
1192
1193       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_REGULAR);
1194
1195       info = g_file_query_info (test_file,
1196                                 G_FILE_ATTRIBUTE_STANDARD_SIZE ","
1197                                 G_FILE_ATTRIBUTE_UNIX_MODE,
1198                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1199       g_assert_no_error (local_error);
1200
1201       if (expected_type == FILE_TEST_SETUP_TYPE_REGULAR_EMPTY)
1202         g_assert_cmpint (g_file_info_get_size (info), ==, 0);
1203       else
1204         g_assert_cmpint (g_file_info_get_size (info), >, 0);
1205
1206       if (expected_contents != NULL)
1207         {
1208           gchar *contents = NULL;
1209           gsize length = 0;
1210
1211           g_file_get_contents (g_file_peek_path (test_file), &contents, &length, &local_error);
1212           g_assert_no_error (local_error);
1213
1214           g_assert_cmpstr (contents, ==, expected_contents);
1215           g_free (contents);
1216         }
1217
1218       g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777, ==, expected_mode);
1219
1220       break;
1221     case FILE_TEST_SETUP_TYPE_DIRECTORY:
1222       g_assert (expected_mode == 0);
1223       g_assert (expected_contents == NULL);
1224
1225       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_DIRECTORY);
1226       break;
1227     case FILE_TEST_SETUP_TYPE_SOCKET:
1228       g_assert (expected_contents == NULL);
1229
1230       g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_SPECIAL);
1231
1232       info = g_file_query_info (test_file,
1233                                 G_FILE_ATTRIBUTE_UNIX_MODE,
1234                                 G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1235       g_assert_no_error (local_error);
1236
1237       g_assert_cmpuint (g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777, ==, expected_mode);
1238       break;
1239     case FILE_TEST_SETUP_TYPE_SYMLINK_VALID:
1240     case FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING:
1241         {
1242           GFile *symlink_target_file = NULL;
1243
1244           /* Permissions on a symlink are not used by the kernel, so are only
1245            * applicable if the symlink is valid (and are applied to the target) */
1246           g_assert (expected_type != FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING || expected_mode == 0);
1247           g_assert (expected_contents != NULL);
1248
1249           g_assert_cmpint (test_file_type, ==, G_FILE_TYPE_SYMBOLIC_LINK);
1250
1251           info = g_file_query_info (test_file,
1252                                     G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
1253                                     G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1254           g_assert_no_error (local_error);
1255
1256           g_assert_cmpstr (g_file_info_get_symlink_target (info), ==, expected_contents);
1257
1258           symlink_target_file = g_file_get_child (tmpdir, g_file_info_get_symlink_target (info));
1259           if (expected_type == FILE_TEST_SETUP_TYPE_SYMLINK_VALID)
1260             g_assert_true (g_file_query_exists (symlink_target_file, NULL));
1261           else
1262             g_assert_false (g_file_query_exists (symlink_target_file, NULL));
1263
1264           if (expected_type == FILE_TEST_SETUP_TYPE_SYMLINK_VALID)
1265             {
1266               GFileInfo *target_info = NULL;
1267
1268               /* Need to re-query the info so we follow symlinks */
1269               target_info = g_file_query_info (test_file,
1270                                                G_FILE_ATTRIBUTE_UNIX_MODE,
1271                                                G_FILE_QUERY_INFO_NONE, NULL, &local_error);
1272               g_assert_no_error (local_error);
1273
1274               g_assert_cmpuint (g_file_info_get_attribute_uint32 (target_info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777, ==, expected_mode);
1275
1276               g_clear_object (&target_info);
1277             }
1278
1279           g_clear_object (&symlink_target_file);
1280           break;
1281         }
1282     default:
1283       g_assert_not_reached ();
1284     }
1285
1286   g_clear_object (&info);
1287   g_clear_object (&target_file);
1288   g_free (target_basename);
1289 }
1290
1291 #endif  /* __linux__ */
1292
1293 /* A big test for g_file_replace() and g_file_replace_readwrite(). The
1294  * @test_data is a boolean: %TRUE to test g_file_replace_readwrite(), %FALSE to
1295  * test g_file_replace(). The test setup and checks are identical for both
1296  * functions; in the case of testing g_file_replace_readwrite(), only the output
1297  * stream side of the returned #GIOStream is tested. i.e. We test the write
1298  * behaviour of both functions is identical.
1299  *
1300  * This is intended to test all static behaviour of the function: for each test
1301  * scenario, a temporary directory is set up with a source file (and maybe some
1302  * other files) in a set configuration, g_file_replace{,_readwrite}() is called,
1303  * and the final state of the directory is checked.
1304  *
1305  * This test does not check dynamic behaviour or race conditions. For example,
1306  * it does not test what happens if the source file is deleted from another
1307  * process half-way through a call to g_file_replace().
1308  */
1309 static void
1310 test_replace (gconstpointer test_data)
1311 {
1312 #ifdef __linux__
1313   gboolean read_write = GPOINTER_TO_UINT (test_data);
1314   const gchar *new_contents = "this is a new test message which should be written to source";
1315   const gchar *original_source_contents = "this is some test content in source";
1316   const gchar *original_backup_contents = "this is some test content in source~";
1317   mode_t current_umask = umask (0);
1318   guint32 default_public_mode = 0666 & ~current_umask;
1319   guint32 default_private_mode = 0600;
1320
1321   const struct
1322     {
1323       /* Arguments to pass to g_file_replace(). */
1324       gboolean replace_make_backup;
1325       GFileCreateFlags replace_flags;
1326       const gchar *replace_etag;  /* (nullable) */
1327
1328       /* File system setup. */
1329       FileTestSetupType setup_source_type;
1330       guint setup_source_mode;
1331       FileTestSetupType setup_backup_type;
1332       guint setup_backup_mode;
1333
1334       /* Expected results. */
1335       gboolean expected_success;
1336       GQuark expected_error_domain;
1337       gint expected_error_code;
1338
1339       /* Expected final file system state. */
1340       guint expected_n_files;
1341       FileTestSetupType expected_source_type;
1342       guint expected_source_mode;
1343       const gchar *expected_source_contents;  /* content for a regular file, or target for a symlink; NULL otherwise */
1344       FileTestSetupType expected_backup_type;
1345       guint expected_backup_mode;
1346       const gchar *expected_backup_contents;  /* content for a regular file, or target for a symlink; NULL otherwise */
1347     }
1348   tests[] =
1349     {
1350       /* replace_make_backup == FALSE, replace_flags == NONE, replace_etag == NULL,
1351        * all the different values of setup_source_type, mostly with a backup
1352        * file created to check it’s not modified */
1353       {
1354         FALSE, G_FILE_CREATE_NONE, NULL,
1355         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1356         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1357         TRUE, 0, 0,
1358         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1359         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1360       },
1361       {
1362         FALSE, G_FILE_CREATE_NONE, NULL,
1363         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1364         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1365         TRUE, 0, 0,
1366         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1367         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1368       },
1369       {
1370         FALSE, G_FILE_CREATE_NONE, NULL,
1371         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1372         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1373         TRUE, 0, 0,
1374         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1375         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1376       },
1377       {
1378         FALSE, G_FILE_CREATE_NONE, NULL,
1379         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1380         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1381         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1382         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1383         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1384       },
1385       {
1386         FALSE, G_FILE_CREATE_NONE, NULL,
1387         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1388         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1389         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1390         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1391         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1392       },
1393       {
1394         FALSE, G_FILE_CREATE_NONE, NULL,
1395         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1396         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1397         TRUE, 0, 0,
1398         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1399         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1400       },
1401       {
1402         FALSE, G_FILE_CREATE_NONE, NULL,
1403         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1404         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1405         TRUE, 0, 0,
1406         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1407         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1408       },
1409
1410       /* replace_etag set to an invalid value, with setup_source_type as a
1411        * regular non-empty file; replacement should fail */
1412       {
1413         FALSE, G_FILE_CREATE_NONE, "incorrect etag",
1414         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1415         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1416         FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
1417         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1418         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1419       },
1420
1421       /* replace_make_backup == TRUE, replace_flags == NONE, replace_etag == NULL,
1422        * all the different values of setup_source_type, with a backup
1423        * file created to check it’s either replaced or the operation fails */
1424       {
1425         TRUE, G_FILE_CREATE_NONE, NULL,
1426         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1427         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1428         TRUE, 0, 0,
1429         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1430         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1431       },
1432       {
1433         TRUE, G_FILE_CREATE_NONE, NULL,
1434         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1435         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1436         TRUE, 0, 0,
1437         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1438         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, NULL,
1439       },
1440       {
1441         TRUE, G_FILE_CREATE_NONE, NULL,
1442         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1443         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1444         TRUE, 0, 0,
1445         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1446         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1447       },
1448       {
1449         TRUE, G_FILE_CREATE_NONE, NULL,
1450         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1451         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1452         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1453         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1454         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1455       },
1456       {
1457         TRUE, G_FILE_CREATE_NONE, NULL,
1458         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1459         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1460         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1461         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1462         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1463       },
1464       {
1465         TRUE, G_FILE_CREATE_NONE, NULL,
1466         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1467         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1468         TRUE, 0, 0,
1469         /* The final situation here is a bit odd; the backup file is a bit
1470          * pointless as the original source file was a dangling symlink.
1471          * Theoretically the backup file should be that symlink, pointing to
1472          * `source-target`, and hence no longer dangling, as that file has now
1473          * been created as the new source content, since REPLACE_DESTINATION was
1474          * not specified. However, the code instead creates an empty regular
1475          * file as the backup. FIXME: This seems acceptable for now, but not
1476          * entirely ideal and would be good to fix at some point. */
1477         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1478         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0777 & ~current_umask, NULL,
1479       },
1480       {
1481         TRUE, G_FILE_CREATE_NONE, NULL,
1482         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1483         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1484         TRUE, 0, 0,
1485         /* FIXME: The permissions for the backup file are just the default umask,
1486          * but should probably be the same as the permissions for the source
1487          * file (`default_public_mode`). This probably arises from the fact that
1488          * symlinks don’t have permissions. */
1489         3, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1490         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, 0777 & ~current_umask, "target file",
1491       },
1492
1493       /* replace_make_backup == TRUE, replace_flags == NONE, replace_etag == NULL,
1494        * setup_source_type is a regular file, with a backup file of every type
1495        * created to check it’s either replaced or the operation fails */
1496       {
1497         TRUE, G_FILE_CREATE_NONE, NULL,
1498         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1499         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1500         TRUE, 0, 0,
1501         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1502         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1503       },
1504       {
1505         TRUE, G_FILE_CREATE_NONE, NULL,
1506         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1507         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1508         TRUE, 0, 0,
1509         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1510         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1511       },
1512       {
1513         TRUE, G_FILE_CREATE_NONE, NULL,
1514         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1515         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1516         TRUE, 0, 0,
1517         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1518         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1519       },
1520       {
1521         TRUE, G_FILE_CREATE_NONE, NULL,
1522         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1523         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1524         FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
1525         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1526         FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1527       },
1528       {
1529         TRUE, G_FILE_CREATE_NONE, NULL,
1530         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1531         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1532         TRUE, 0, 0,
1533         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1534         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1535       },
1536       {
1537         TRUE, G_FILE_CREATE_NONE, NULL,
1538         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1539         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1540         TRUE, 0, 0,
1541         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1542         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1543       },
1544       {
1545         TRUE, G_FILE_CREATE_NONE, NULL,
1546         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1547         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1548         TRUE, 0, 0,
1549         /* the third file is `source~-target`, the original target of the old
1550          * backup symlink */
1551         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1552         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1553       },
1554
1555       /* replace_make_backup == FALSE, replace_flags == REPLACE_DESTINATION,
1556        * replace_etag == NULL, all the different values of setup_source_type,
1557        * mostly with a backup file created to check it’s not modified */
1558       {
1559         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1560         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1561         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1562         TRUE, 0, 0,
1563         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1564         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1565       },
1566       {
1567         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1568         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1569         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1570         TRUE, 0, 0,
1571         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1572         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1573       },
1574       {
1575         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1576         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1577         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1578         TRUE, 0, 0,
1579         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1580         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1581       },
1582       {
1583         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1584         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1585         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1586         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1587         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1588         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1589       },
1590       {
1591         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1592         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1593         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1594         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1595         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1596         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1597       },
1598       {
1599         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1600         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1601         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1602         TRUE, 0, 0,
1603         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1604         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1605       },
1606       {
1607         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1608         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1609         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1610         TRUE, 0, 0,
1611         /* the third file is `source-target`, the original target of the old
1612          * source file */
1613         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1614         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1615       },
1616
1617       /* replace_flags == REPLACE_DESTINATION, replace_etag set to an invalid
1618        * value, with setup_source_type as a regular non-empty file; replacement
1619        * should fail */
1620       {
1621         FALSE, G_FILE_CREATE_REPLACE_DESTINATION, "incorrect etag",
1622         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1623         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1624         FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
1625         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1626         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1627       },
1628
1629       /* replace_make_backup == TRUE, replace_flags == REPLACE_DESTINATION,
1630        * replace_etag == NULL, all the different values of setup_source_type,
1631        * with a backup file created to check it’s either replaced or the
1632        * operation fails */
1633       {
1634         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1635         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1636         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1637         TRUE, 0, 0,
1638         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1639         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1640       },
1641       {
1642         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1643         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1644         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1645         TRUE, 0, 0,
1646         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1647         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, NULL,
1648       },
1649       {
1650         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1651         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1652         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1653         TRUE, 0, 0,
1654         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1655         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1656       },
1657       {
1658         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1659         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1660         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1661         FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
1662         2, FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1663         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1664       },
1665       {
1666         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1667         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1668         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1669         FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
1670         2, FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, NULL,
1671         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_backup_contents,
1672       },
1673       {
1674         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1675         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1676         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1677         TRUE, 0, 0,
1678         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1679         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, "source-target",
1680       },
1681       {
1682         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1683         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1684         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1685         TRUE, 0, 0,
1686         /* the third file is `source-target`, the original target of the old
1687          * source file */
1688         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1689         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
1690       },
1691
1692       /* replace_make_backup == TRUE, replace_flags == REPLACE_DESTINATION,
1693        * replace_etag == NULL, setup_source_type is a regular file, with a
1694        * backup file of every type created to check it’s either replaced or the
1695        * operation fails */
1696       {
1697         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1698         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1699         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1700         TRUE, 0, 0,
1701         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1702         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1703       },
1704       {
1705         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1706         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1707         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
1708         TRUE, 0, 0,
1709         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1710         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1711       },
1712       {
1713         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1714         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1715         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1716         TRUE, 0, 0,
1717         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1718         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1719       },
1720       {
1721         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1722         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1723         FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
1724         FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
1725         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1726         FILE_TEST_SETUP_TYPE_DIRECTORY, 0, NULL,
1727       },
1728       {
1729         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1730         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1731         FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
1732         TRUE, 0, 0,
1733         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1734         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1735       },
1736       {
1737         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1738         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1739         FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
1740         TRUE, 0, 0,
1741         2, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1742         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1743       },
1744       {
1745         TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1746         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1747         FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
1748         TRUE, 0, 0,
1749         /* the third file is `source~-target`, the original target of the old
1750          * backup symlink */
1751         3, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1752         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, original_source_contents,
1753       },
1754
1755       /* several different setups with replace_flags == PRIVATE */
1756       {
1757         FALSE, G_FILE_CREATE_PRIVATE, NULL,
1758         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1759         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1760         TRUE, 0, 0,
1761         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents,
1762         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1763       },
1764       {
1765         FALSE, G_FILE_CREATE_PRIVATE, NULL,
1766         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1767         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1768         TRUE, 0, 0,
1769         /* the file isn’t being replaced, so it should keep its existing permissions */
1770         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, new_contents,
1771         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1772       },
1773       {
1774         FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1775         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1776         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1777         TRUE, 0, 0,
1778         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents,
1779         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1780       },
1781       {
1782         FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL,
1783         FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
1784         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1785         TRUE, 0, 0,
1786         1, FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_private_mode, new_contents,
1787         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1788       },
1789
1790       /* make the initial source file unreadable, so the replace operation
1791        * should fail */
1792       {
1793         FALSE, G_FILE_CREATE_NONE, NULL,
1794         FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0  /* most restrictive permissions */,
1795         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
1796         FALSE, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
1797         1, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0, NULL,
1798         FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
1799       },
1800     };
1801   gsize i;
1802
1803   g_test_summary ("Test various situations for g_file_replace()");
1804
1805   /* Reset the umask after querying it above. There’s no way to query it without
1806    * changing it. */
1807   umask (current_umask);
1808   g_test_message ("Current umask: %u", current_umask);
1809
1810   for (i = 0; i < G_N_ELEMENTS (tests); i++)
1811     {
1812       gchar *tmpdir_path = NULL;
1813       GFile *tmpdir = NULL, *source_file = NULL, *backup_file = NULL;
1814       GFileOutputStream *output_stream = NULL;
1815       GFileIOStream *io_stream = NULL;
1816       GFileEnumerator *enumerator = NULL;
1817       GFileInfo *info = NULL;
1818       guint n_files;
1819       GError *local_error = NULL;
1820
1821       /* Create a fresh, empty working directory. */
1822       tmpdir_path = g_dir_make_tmp ("g_file_replace_XXXXXX", &local_error);
1823       g_assert_no_error (local_error);
1824       tmpdir = g_file_new_for_path (tmpdir_path);
1825
1826       g_test_message ("Test %" G_GSIZE_FORMAT ", using temporary directory %s", i, tmpdir_path);
1827       g_free (tmpdir_path);
1828
1829       /* Set up the test directory. */
1830       source_file = create_test_file (tmpdir, "source", tests[i].setup_source_type, tests[i].setup_source_mode);
1831       backup_file = create_test_file (tmpdir, "source~", tests[i].setup_backup_type, tests[i].setup_backup_mode);
1832
1833       /* Replace the source file. Check the error state only after finishing
1834        * writing, as the replace operation is split across g_file_replace() and
1835        * g_output_stream_close(). */
1836       if (read_write)
1837         io_stream = g_file_replace_readwrite (source_file,
1838                                               tests[i].replace_etag,
1839                                               tests[i].replace_make_backup,
1840                                               tests[i].replace_flags,
1841                                               NULL,
1842                                               &local_error);
1843       else
1844         output_stream = g_file_replace (source_file,
1845                                         tests[i].replace_etag,
1846                                         tests[i].replace_make_backup,
1847                                         tests[i].replace_flags,
1848                                         NULL,
1849                                         &local_error);
1850
1851       if (tests[i].expected_success)
1852         {
1853           g_assert_no_error (local_error);
1854           if (read_write)
1855             g_assert_nonnull (io_stream);
1856           else
1857             g_assert_nonnull (output_stream);
1858         }
1859
1860       /* Write new content to it. */
1861       if (io_stream != NULL)
1862         {
1863           GOutputStream *io_output_stream = g_io_stream_get_output_stream (G_IO_STREAM (io_stream));
1864           gsize n_written;
1865
1866           g_output_stream_write_all (G_OUTPUT_STREAM (io_output_stream), new_contents, strlen (new_contents),
1867                                      &n_written, NULL, &local_error);
1868
1869           if (tests[i].expected_success)
1870             {
1871               g_assert_no_error (local_error);
1872               g_assert_cmpint (n_written, ==, strlen (new_contents));
1873             }
1874
1875           g_io_stream_close (G_IO_STREAM (io_stream), NULL, (local_error == NULL) ? &local_error : NULL);
1876
1877           if (tests[i].expected_success)
1878             g_assert_no_error (local_error);
1879         }
1880       else if (output_stream != NULL)
1881         {
1882           gsize n_written;
1883
1884           g_output_stream_write_all (G_OUTPUT_STREAM (output_stream), new_contents, strlen (new_contents),
1885                                      &n_written, NULL, &local_error);
1886
1887           if (tests[i].expected_success)
1888             {
1889               g_assert_no_error (local_error);
1890               g_assert_cmpint (n_written, ==, strlen (new_contents));
1891             }
1892
1893           g_output_stream_close (G_OUTPUT_STREAM (output_stream), NULL, (local_error == NULL) ? &local_error : NULL);
1894
1895           if (tests[i].expected_success)
1896             g_assert_no_error (local_error);
1897         }
1898
1899       if (tests[i].expected_success)
1900         g_assert_no_error (local_error);
1901       else
1902         g_assert_error (local_error, tests[i].expected_error_domain, tests[i].expected_error_code);
1903
1904       g_clear_error (&local_error);
1905       g_clear_object (&io_stream);
1906       g_clear_object (&output_stream);
1907
1908       /* Verify the final state of the directory. */
1909       enumerator = g_file_enumerate_children (tmpdir,
1910                                               G_FILE_ATTRIBUTE_STANDARD_NAME,
1911                                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, &local_error);
1912       g_assert_no_error (local_error);
1913
1914       n_files = 0;
1915       do
1916         {
1917           g_file_enumerator_iterate (enumerator, &info, NULL, NULL, &local_error);
1918           g_assert_no_error (local_error);
1919
1920           if (info != NULL)
1921             n_files++;
1922         }
1923       while (info != NULL);
1924
1925       g_clear_object (&enumerator);
1926
1927       g_assert_cmpuint (n_files, ==, tests[i].expected_n_files);
1928
1929       check_test_file (source_file, tmpdir, "source", tests[i].expected_source_type, tests[i].expected_source_mode, tests[i].expected_source_contents);
1930       check_test_file (backup_file, tmpdir, "source~", tests[i].expected_backup_type, tests[i].expected_backup_mode, tests[i].expected_backup_contents);
1931
1932       /* Tidy up. Ignore failure apart from when deleting the directory, which
1933        * should be empty. */
1934       g_file_delete (source_file, NULL, NULL);
1935       g_file_delete (backup_file, NULL, NULL);
1936
1937       /* Other files which are occasionally generated by the tests. */
1938         {
1939           GFile *backup_target_file = g_file_get_child (tmpdir, "source~-target");
1940           g_file_delete (backup_target_file, NULL, NULL);
1941           g_clear_object (&backup_target_file);
1942         }
1943         {
1944           GFile *backup_target_file = g_file_get_child (tmpdir, "source-target");
1945           g_file_delete (backup_target_file, NULL, NULL);
1946           g_clear_object (&backup_target_file);
1947         }
1948
1949       g_file_delete (tmpdir, NULL, &local_error);
1950       g_assert_no_error (local_error);
1951
1952       g_clear_object (&backup_file);
1953       g_clear_object (&source_file);
1954       g_clear_object (&tmpdir);
1955     }
1956 #else  /* if !__linux__ */
1957   g_test_skip ("File replacement tests can only be run on Linux");
1958 #endif
1959 }
1960
1961 static void
1962 on_file_deleted (GObject      *object,
1963                  GAsyncResult *result,
1964                  gpointer      user_data)
1965 {
1966   GError *local_error = NULL;
1967   GMainLoop *loop = user_data;
1968
1969   (void) g_file_delete_finish ((GFile*)object, result, &local_error);
1970   g_assert_no_error (local_error);
1971
1972   g_main_loop_quit (loop);
1973 }
1974
1975 static void
1976 test_async_delete (void)
1977 {
1978   GFile *file;
1979   GFileIOStream *iostream;
1980   GError *local_error = NULL;
1981   GError **error = &local_error;
1982   GMainLoop *loop;
1983
1984   file = g_file_new_tmp ("g_file_delete_XXXXXX",
1985                          &iostream, error);
1986   g_assert_no_error (local_error);
1987   g_object_unref (iostream);
1988
1989   g_assert_true (g_file_query_exists (file, NULL));
1990
1991   loop = g_main_loop_new (NULL, TRUE);
1992
1993   g_file_delete_async (file, G_PRIORITY_DEFAULT, NULL, on_file_deleted, loop);
1994
1995   g_main_loop_run (loop);
1996
1997   g_assert_false (g_file_query_exists (file, NULL));
1998
1999   g_main_loop_unref (loop);
2000   g_object_unref (file);
2001 }
2002
2003 static void
2004 test_copy_preserve_mode (void)
2005 {
2006 #ifdef G_OS_UNIX
2007   mode_t current_umask = umask (0);
2008   const struct
2009     {
2010       guint32 source_mode;
2011       guint32 expected_destination_mode;
2012       gboolean create_destination_before_copy;
2013       GFileCopyFlags copy_flags;
2014     }
2015   vectors[] =
2016     {
2017       /* Overwriting the destination file should copy the permissions from the
2018        * source file, even if %G_FILE_COPY_ALL_METADATA is set: */
2019       { 0600, 0600, TRUE, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2020       { 0600, 0600, TRUE, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS },
2021       /* The same behaviour should hold if the destination file is not being
2022        * overwritten because it doesn’t already exist: */
2023       { 0600, 0600, FALSE, G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2024       { 0600, 0600, FALSE, G_FILE_COPY_NOFOLLOW_SYMLINKS },
2025       /* Anything with %G_FILE_COPY_TARGET_DEFAULT_PERMS should use the current
2026        * umask for the destination file: */
2027       { 0600, 0666 & ~current_umask, TRUE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2028       { 0600, 0666 & ~current_umask, TRUE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS },
2029       { 0600, 0666 & ~current_umask, FALSE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA },
2030       { 0600, 0666 & ~current_umask, FALSE, G_FILE_COPY_TARGET_DEFAULT_PERMS | G_FILE_COPY_NOFOLLOW_SYMLINKS },
2031     };
2032   gsize i;
2033
2034   /* Reset the umask after querying it above. There’s no way to query it without
2035    * changing it. */
2036   umask (current_umask);
2037   g_test_message ("Current umask: %u", current_umask);
2038
2039   for (i = 0; i < G_N_ELEMENTS (vectors); i++)
2040     {
2041       GFile *tmpfile;
2042       GFile *dest_tmpfile;
2043       GFileInfo *dest_info;
2044       GFileIOStream *iostream;
2045       GError *local_error = NULL;
2046       guint32 romode = vectors[i].source_mode;
2047       guint32 dest_mode;
2048
2049       g_test_message ("Vector %" G_GSIZE_FORMAT, i);
2050
2051       tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
2052                                 &iostream, &local_error);
2053       g_assert_no_error (local_error);
2054       g_io_stream_close ((GIOStream*)iostream, NULL, &local_error);
2055       g_assert_no_error (local_error);
2056       g_clear_object (&iostream);
2057
2058       g_file_set_attribute (tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_ATTRIBUTE_TYPE_UINT32,
2059                             &romode, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2060                             NULL, &local_error);
2061       g_assert_no_error (local_error);
2062
2063       dest_tmpfile = g_file_new_tmp ("tmp-copy-preserve-modeXXXXXX",
2064                                      &iostream, &local_error);
2065       g_assert_no_error (local_error);
2066       g_io_stream_close ((GIOStream*)iostream, NULL, &local_error);
2067       g_assert_no_error (local_error);
2068       g_clear_object (&iostream);
2069
2070       if (!vectors[i].create_destination_before_copy)
2071         {
2072           g_file_delete (dest_tmpfile, NULL, &local_error);
2073           g_assert_no_error (local_error);
2074         }
2075
2076       g_file_copy (tmpfile, dest_tmpfile, vectors[i].copy_flags,
2077                    NULL, NULL, NULL, &local_error);
2078       g_assert_no_error (local_error);
2079
2080       dest_info = g_file_query_info (dest_tmpfile, G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
2081                                      NULL, &local_error);
2082       g_assert_no_error (local_error);
2083
2084       dest_mode = g_file_info_get_attribute_uint32 (dest_info, G_FILE_ATTRIBUTE_UNIX_MODE);
2085
2086       g_assert_cmpint (dest_mode & ~S_IFMT, ==, vectors[i].expected_destination_mode);
2087       g_assert_cmpint (dest_mode & S_IFMT, ==, S_IFREG);
2088
2089       (void) g_file_delete (tmpfile, NULL, NULL);
2090       (void) g_file_delete (dest_tmpfile, NULL, NULL);
2091
2092       g_clear_object (&tmpfile);
2093       g_clear_object (&dest_tmpfile);
2094       g_clear_object (&dest_info);
2095     }
2096 #else  /* if !G_OS_UNIX */
2097   g_test_skip ("File permissions tests can only be run on Unix")
2098 #endif
2099 }
2100
2101 static gchar *
2102 splice_to_string (GInputStream   *stream,
2103                   GError        **error)
2104 {
2105   GMemoryOutputStream *buffer = NULL;
2106   char *ret = NULL;
2107
2108   buffer = (GMemoryOutputStream*)g_memory_output_stream_new (NULL, 0, g_realloc, g_free);
2109   if (g_output_stream_splice ((GOutputStream*)buffer, stream, 0, NULL, error) < 0)
2110     goto out;
2111
2112   if (!g_output_stream_write ((GOutputStream*)buffer, "\0", 1, NULL, error))
2113     goto out;
2114
2115   if (!g_output_stream_close ((GOutputStream*)buffer, NULL, error))
2116     goto out;
2117
2118   ret = g_memory_output_stream_steal_data (buffer);
2119  out:
2120   g_clear_object (&buffer);
2121   return ret;
2122 }
2123
2124 static gboolean
2125 get_size_from_du (const gchar *path, guint64 *size)
2126 {
2127   GSubprocess *du;
2128   gboolean ok;
2129   gchar *result;
2130   gchar *endptr;
2131   GError *error = NULL;
2132   gchar *du_path = NULL;
2133
2134   /* If we can’t find du, don’t try and run the test. */
2135   du_path = g_find_program_in_path ("du");
2136   if (du_path == NULL)
2137     return FALSE;
2138   g_free (du_path);
2139
2140   du = g_subprocess_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE,
2141                          &error,
2142                          "du", "--bytes", "-s", path, NULL);
2143   g_assert_no_error (error);
2144
2145   result = splice_to_string (g_subprocess_get_stdout_pipe (du), &error);
2146   g_assert_no_error (error);
2147
2148   *size = g_ascii_strtoll (result, &endptr, 10);
2149
2150   g_subprocess_wait (du, NULL, &error);
2151   g_assert_no_error (error);
2152
2153   ok = g_subprocess_get_successful (du);
2154
2155   g_object_unref (du);
2156   g_free (result);
2157
2158   return ok;
2159 }
2160
2161 static void
2162 test_measure (void)
2163 {
2164   GFile *file;
2165   guint64 size;
2166   guint64 num_bytes;
2167   guint64 num_dirs;
2168   guint64 num_files;
2169   GError *error = NULL;
2170   gboolean ok;
2171   gchar *path;
2172
2173   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
2174   file = g_file_new_for_path (path);
2175
2176   if (!get_size_from_du (path, &size))
2177     {
2178       g_test_message ("du not found or fail to run, skipping byte measurement");
2179       size = 0;
2180     }
2181
2182   ok = g_file_measure_disk_usage (file,
2183                                   G_FILE_MEASURE_APPARENT_SIZE,
2184                                   NULL,
2185                                   NULL,
2186                                   NULL,
2187                                   &num_bytes,
2188                                   &num_dirs,
2189                                   &num_files,
2190                                   &error);
2191   g_assert_true (ok);
2192   g_assert_no_error (error);
2193
2194   if (size > 0)
2195     g_assert_cmpuint (num_bytes, ==, size);
2196   g_assert_cmpuint (num_dirs, ==, 6);
2197   g_assert_cmpuint (num_files, ==, 32);
2198
2199   g_object_unref (file);
2200   g_free (path);
2201 }
2202
2203 typedef struct {
2204   guint64 expected_bytes;
2205   guint64 expected_dirs;
2206   guint64 expected_files;
2207   gint progress_count;
2208   guint64 progress_bytes;
2209   guint64 progress_dirs;
2210   guint64 progress_files;
2211 } MeasureData;
2212
2213 static void
2214 measure_progress (gboolean reporting,
2215                   guint64  current_size,
2216                   guint64  num_dirs,
2217                   guint64  num_files,
2218                   gpointer user_data)
2219 {
2220   MeasureData *data = user_data;
2221
2222   data->progress_count += 1;
2223
2224   g_assert_cmpuint (current_size, >=, data->progress_bytes);
2225   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
2226   g_assert_cmpuint (num_files, >=, data->progress_files);
2227
2228   data->progress_bytes = current_size;
2229   data->progress_dirs = num_dirs;
2230   data->progress_files = num_files;
2231 }
2232
2233 static void
2234 measure_done (GObject      *source,
2235               GAsyncResult *res,
2236               gpointer      user_data)
2237 {
2238   MeasureData *data = user_data;
2239   guint64 num_bytes, num_dirs, num_files;
2240   GError *error = NULL;
2241   gboolean ok;
2242
2243   ok = g_file_measure_disk_usage_finish (G_FILE (source), res, &num_bytes, &num_dirs, &num_files, &error);
2244   g_assert_true (ok);
2245   g_assert_no_error (error);
2246
2247   if (data->expected_bytes > 0)
2248     g_assert_cmpuint (data->expected_bytes, ==, num_bytes);
2249   g_assert_cmpuint (data->expected_dirs, ==, num_dirs);
2250   g_assert_cmpuint (data->expected_files, ==, num_files);
2251
2252   g_assert_cmpuint (data->progress_count, >, 0);
2253   g_assert_cmpuint (num_bytes, >=, data->progress_bytes);
2254   g_assert_cmpuint (num_dirs, >=, data->progress_dirs);
2255   g_assert_cmpuint (num_files, >=, data->progress_files);
2256
2257   g_free (data);
2258   g_object_unref (source);
2259 }
2260
2261 static void
2262 test_measure_async (void)
2263 {
2264   gchar *path;
2265   GFile *file;
2266   MeasureData *data;
2267
2268   data = g_new (MeasureData, 1);
2269
2270   data->progress_count = 0;
2271   data->progress_bytes = 0;
2272   data->progress_files = 0;
2273   data->progress_dirs = 0;
2274
2275   path = g_test_build_filename (G_TEST_DIST, "desktop-files", NULL);
2276   file = g_file_new_for_path (path);
2277
2278   if (!get_size_from_du (path, &data->expected_bytes))
2279     {
2280       g_test_message ("du not found or fail to run, skipping byte measurement");
2281       data->expected_bytes = 0;
2282     }
2283
2284   g_free (path);
2285
2286   data->expected_dirs = 6;
2287   data->expected_files = 32;
2288
2289   g_file_measure_disk_usage_async (file,
2290                                    G_FILE_MEASURE_APPARENT_SIZE,
2291                                    0, NULL,
2292                                    measure_progress, data,
2293                                    measure_done, data);
2294 }
2295
2296 static void
2297 test_load_bytes (void)
2298 {
2299   gchar filename[] = "g_file_load_bytes_XXXXXX";
2300   GError *error = NULL;
2301   GBytes *bytes;
2302   GFile *file;
2303   int len;
2304   int fd;
2305   int ret;
2306
2307   fd = g_mkstemp (filename);
2308   g_assert_cmpint (fd, !=, -1);
2309   len = strlen ("test_load_bytes");
2310   ret = write (fd, "test_load_bytes", len);
2311   g_assert_cmpint (ret, ==, len);
2312   close (fd);
2313
2314   file = g_file_new_for_path (filename);
2315   bytes = g_file_load_bytes (file, NULL, NULL, &error);
2316   g_assert_no_error (error);
2317   g_assert_nonnull (bytes);
2318   g_assert_cmpint (len, ==, g_bytes_get_size (bytes));
2319   g_assert_cmpstr ("test_load_bytes", ==, (gchar *)g_bytes_get_data (bytes, NULL));
2320
2321   g_file_delete (file, NULL, NULL);
2322
2323   g_bytes_unref (bytes);
2324   g_object_unref (file);
2325 }
2326
2327 typedef struct
2328 {
2329   GMainLoop *main_loop;
2330   GFile *file;
2331   GBytes *bytes;
2332 } LoadBytesAsyncData;
2333
2334 static void
2335 test_load_bytes_cb (GObject      *object,
2336                     GAsyncResult *result,
2337                     gpointer      user_data)
2338 {
2339   GFile *file = G_FILE (object);
2340   LoadBytesAsyncData *data = user_data;
2341   GError *error = NULL;
2342
2343   data->bytes = g_file_load_bytes_finish (file, result, NULL, &error);
2344   g_assert_no_error (error);
2345   g_assert_nonnull (data->bytes);
2346
2347   g_main_loop_quit (data->main_loop);
2348 }
2349
2350 static void
2351 test_load_bytes_async (void)
2352 {
2353   LoadBytesAsyncData data = { 0 };
2354   gchar filename[] = "g_file_load_bytes_XXXXXX";
2355   int len;
2356   int fd;
2357   int ret;
2358
2359   fd = g_mkstemp (filename);
2360   g_assert_cmpint (fd, !=, -1);
2361   len = strlen ("test_load_bytes_async");
2362   ret = write (fd, "test_load_bytes_async", len);
2363   g_assert_cmpint (ret, ==, len);
2364   close (fd);
2365
2366   data.main_loop = g_main_loop_new (NULL, FALSE);
2367   data.file = g_file_new_for_path (filename);
2368
2369   g_file_load_bytes_async (data.file, NULL, test_load_bytes_cb, &data);
2370   g_main_loop_run (data.main_loop);
2371
2372   g_assert_cmpint (len, ==, g_bytes_get_size (data.bytes));
2373   g_assert_cmpstr ("test_load_bytes_async", ==, (gchar *)g_bytes_get_data (data.bytes, NULL));
2374
2375   g_file_delete (data.file, NULL, NULL);
2376   g_object_unref (data.file);
2377   g_bytes_unref (data.bytes);
2378   g_main_loop_unref (data.main_loop);
2379 }
2380
2381 static void
2382 test_writev_helper (GOutputVector *vectors,
2383                     gsize          n_vectors,
2384                     gboolean       use_bytes_written,
2385                     const guint8  *expected_contents,
2386                     gsize          expected_length)
2387 {
2388   GFile *file;
2389   GFileIOStream *iostream = NULL;
2390   GOutputStream *ostream;
2391   GError *error = NULL;
2392   gsize bytes_written = 0;
2393   gboolean res;
2394   guint8 *contents;
2395   gsize length;
2396
2397   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2398                          &iostream, NULL);
2399   g_assert_nonnull (file);
2400   g_assert_nonnull (iostream);
2401
2402   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2403
2404   res = g_output_stream_writev_all (ostream, vectors, n_vectors, use_bytes_written ? &bytes_written : NULL, NULL, &error);
2405   g_assert_no_error (error);
2406   g_assert_true (res);
2407   if (use_bytes_written)
2408     g_assert_cmpuint (bytes_written, ==, expected_length);
2409
2410   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2411   g_assert_no_error (error);
2412   g_assert_true (res);
2413   g_object_unref (iostream);
2414
2415   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2416   g_assert_no_error (error);
2417   g_assert_true (res);
2418
2419   g_assert_cmpmem (contents, length, expected_contents, expected_length);
2420
2421   g_free (contents);
2422
2423   g_file_delete (file, NULL, NULL);
2424   g_object_unref (file);
2425 }
2426
2427 /* Test that writev() on local file output streams works on a non-empty vector */
2428 static void
2429 test_writev (void)
2430 {
2431   GOutputVector vectors[3];
2432   const guint8 buffer[] = {1, 2, 3, 4, 5,
2433                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2434                            1, 2, 3};
2435
2436   vectors[0].buffer = buffer;
2437   vectors[0].size = 5;
2438
2439   vectors[1].buffer = buffer + 5;
2440   vectors[1].size = 12;
2441
2442   vectors[2].buffer = buffer + 5 + 12;
2443   vectors[2].size = 3;
2444
2445   test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, buffer, sizeof buffer);
2446 }
2447
2448 /* Test that writev() on local file output streams works on a non-empty vector without returning bytes_written */
2449 static void
2450 test_writev_no_bytes_written (void)
2451 {
2452   GOutputVector vectors[3];
2453   const guint8 buffer[] = {1, 2, 3, 4, 5,
2454                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2455                            1, 2, 3};
2456
2457   vectors[0].buffer = buffer;
2458   vectors[0].size = 5;
2459
2460   vectors[1].buffer = buffer + 5;
2461   vectors[1].size = 12;
2462
2463   vectors[2].buffer = buffer + 5 + 12;
2464   vectors[2].size = 3;
2465
2466   test_writev_helper (vectors, G_N_ELEMENTS (vectors), FALSE, buffer, sizeof buffer);
2467 }
2468
2469 /* Test that writev() on local file output streams works on 0 vectors */
2470 static void
2471 test_writev_no_vectors (void)
2472 {
2473   test_writev_helper (NULL, 0, TRUE, NULL, 0);
2474 }
2475
2476 /* Test that writev() on local file output streams works on empty vectors */
2477 static void
2478 test_writev_empty_vectors (void)
2479 {
2480   GOutputVector vectors[3];
2481
2482   vectors[0].buffer = NULL;
2483   vectors[0].size = 0;
2484   vectors[1].buffer = NULL;
2485   vectors[1].size = 0;
2486   vectors[2].buffer = NULL;
2487   vectors[2].size = 0;
2488
2489   test_writev_helper (vectors, G_N_ELEMENTS (vectors), TRUE, NULL, 0);
2490 }
2491
2492 /* Test that writev() fails if the sum of sizes in the vector is too big */
2493 static void
2494 test_writev_too_big_vectors (void)
2495 {
2496   GFile *file;
2497   GFileIOStream *iostream = NULL;
2498   GOutputStream *ostream;
2499   GError *error = NULL;
2500   gsize bytes_written = 0;
2501   gboolean res;
2502   guint8 *contents;
2503   gsize length;
2504   GOutputVector vectors[3];
2505
2506   vectors[0].buffer = (void*) 1;
2507   vectors[0].size = G_MAXSIZE / 2;
2508
2509   vectors[1].buffer = (void*) 1;
2510   vectors[1].size = G_MAXSIZE / 2;
2511
2512   vectors[2].buffer = (void*) 1;
2513   vectors[2].size = G_MAXSIZE / 2;
2514
2515   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2516                          &iostream, NULL);
2517   g_assert_nonnull (file);
2518   g_assert_nonnull (iostream);
2519
2520   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2521
2522   res = g_output_stream_writev_all (ostream, vectors, G_N_ELEMENTS (vectors), &bytes_written, NULL, &error);
2523   g_assert_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
2524   g_assert_cmpuint (bytes_written, ==, 0);
2525   g_assert_false (res);
2526   g_clear_error (&error);
2527
2528   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2529   g_assert_no_error (error);
2530   g_assert_true (res);
2531   g_object_unref (iostream);
2532
2533   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2534   g_assert_no_error (error);
2535   g_assert_true (res);
2536
2537   g_assert_cmpmem (contents, length, NULL, 0);
2538
2539   g_free (contents);
2540
2541   g_file_delete (file, NULL, NULL);
2542   g_object_unref (file);
2543 }
2544
2545 typedef struct
2546 {
2547   gsize bytes_written;
2548   GOutputVector *vectors;
2549   gsize n_vectors;
2550   GError *error;
2551   gboolean done;
2552 } WritevAsyncData;
2553
2554 static void
2555 test_writev_async_cb (GObject      *object,
2556                       GAsyncResult *result,
2557                       gpointer      user_data)
2558 {
2559   GOutputStream *ostream = G_OUTPUT_STREAM (object);
2560   WritevAsyncData *data = user_data;
2561   GError *error = NULL;
2562   gsize bytes_written;
2563   gboolean res;
2564
2565   res = g_output_stream_writev_finish (ostream, result, &bytes_written, &error);
2566   g_assert_true (res);
2567   g_assert_no_error (error);
2568   data->bytes_written += bytes_written;
2569
2570   /* skip vectors that have been written in full */
2571   while (data->n_vectors > 0 && bytes_written >= data->vectors[0].size)
2572     {
2573       bytes_written -= data->vectors[0].size;
2574       ++data->vectors;
2575       --data->n_vectors;
2576     }
2577   /* skip partially written vector data */
2578   if (bytes_written > 0 && data->n_vectors > 0)
2579     {
2580       data->vectors[0].size -= bytes_written;
2581       data->vectors[0].buffer = ((guint8 *) data->vectors[0].buffer) + bytes_written;
2582     }
2583
2584   if (data->n_vectors > 0)
2585     g_output_stream_writev_async (ostream, data->vectors, data->n_vectors, 0, NULL, test_writev_async_cb, &data);
2586 }
2587
2588 /* Test that writev_async() on local file output streams works on a non-empty vector */
2589 static void
2590 test_writev_async (void)
2591 {
2592   WritevAsyncData data = { 0 };
2593   GFile *file;
2594   GFileIOStream *iostream = NULL;
2595   GOutputVector vectors[3];
2596   const guint8 buffer[] = {1, 2, 3, 4, 5,
2597                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2598                            1, 2, 3};
2599   GOutputStream *ostream;
2600   GError *error = NULL;
2601   gboolean res;
2602   guint8 *contents;
2603   gsize length;
2604
2605   vectors[0].buffer = buffer;
2606   vectors[0].size = 5;
2607
2608   vectors[1].buffer = buffer + 5;
2609   vectors[1].size = 12;
2610
2611   vectors[2].buffer = buffer + 5  + 12;
2612   vectors[2].size = 3;
2613
2614   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2615                          &iostream, NULL);
2616   g_assert_nonnull (file);
2617   g_assert_nonnull (iostream);
2618
2619   data.vectors = vectors;
2620   data.n_vectors = G_N_ELEMENTS (vectors);
2621
2622   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2623
2624   g_output_stream_writev_async (ostream, data.vectors, data.n_vectors, 0, NULL, test_writev_async_cb, &data);
2625
2626   while (data.n_vectors > 0)
2627     g_main_context_iteration (NULL, TRUE);
2628
2629   g_assert_cmpuint (data.bytes_written, ==, sizeof buffer);
2630
2631   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2632   g_assert_no_error (error);
2633   g_assert_true (res);
2634   g_object_unref (iostream);
2635
2636   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2637   g_assert_no_error (error);
2638   g_assert_true (res);
2639
2640   g_assert_cmpmem (contents, length, buffer, sizeof buffer);
2641
2642   g_free (contents);
2643
2644   g_file_delete (file, NULL, NULL);
2645   g_object_unref (file);
2646 }
2647
2648 static void
2649 test_writev_all_cb (GObject      *object,
2650                     GAsyncResult *result,
2651                     gpointer      user_data)
2652 {
2653   GOutputStream *ostream = G_OUTPUT_STREAM (object);
2654   WritevAsyncData *data = user_data;
2655
2656   g_output_stream_writev_all_finish (ostream, result, &data->bytes_written, &data->error);
2657   data->done = TRUE;
2658 }
2659
2660 /* Test that writev_async_all() on local file output streams works on a non-empty vector */
2661 static void
2662 test_writev_async_all (void)
2663 {
2664   WritevAsyncData data = { 0 };
2665   GFile *file;
2666   GFileIOStream *iostream = NULL;
2667   GOutputStream *ostream;
2668   GOutputVector vectors[3];
2669   const guint8 buffer[] = {1, 2, 3, 4, 5,
2670                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2671                            1, 2, 3};
2672   GError *error = NULL;
2673   gboolean res;
2674   guint8 *contents;
2675   gsize length;
2676
2677   vectors[0].buffer = buffer;
2678   vectors[0].size = 5;
2679
2680   vectors[1].buffer = buffer + 5;
2681   vectors[1].size = 12;
2682
2683   vectors[2].buffer = buffer + 5  + 12;
2684   vectors[2].size = 3;
2685
2686   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2687                          &iostream, NULL);
2688   g_assert_nonnull (file);
2689   g_assert_nonnull (iostream);
2690
2691   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2692
2693   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data);
2694
2695   while (!data.done)
2696     g_main_context_iteration (NULL, TRUE);
2697
2698   g_assert_cmpuint (data.bytes_written, ==, sizeof buffer);
2699   g_assert_no_error (data.error);
2700
2701   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2702   g_assert_no_error (error);
2703   g_assert_true (res);
2704   g_object_unref (iostream);
2705
2706   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2707   g_assert_no_error (error);
2708   g_assert_true (res);
2709
2710   g_assert_cmpmem (contents, length, buffer, sizeof buffer);
2711
2712   g_free (contents);
2713
2714   g_file_delete (file, NULL, NULL);
2715   g_object_unref (file);
2716 }
2717
2718 /* Test that writev_async_all() on local file output streams handles cancellation correctly */
2719 static void
2720 test_writev_async_all_cancellation (void)
2721 {
2722   WritevAsyncData data = { 0 };
2723   GFile *file;
2724   GFileIOStream *iostream = NULL;
2725   GOutputVector vectors[3];
2726   const guint8 buffer[] = {1, 2, 3, 4, 5,
2727                            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
2728                            1, 2, 3};
2729   GOutputStream *ostream;
2730   GError *error = NULL;
2731   gboolean res;
2732   guint8 *contents;
2733   gsize length;
2734   GCancellable *cancellable;
2735
2736   vectors[0].buffer = buffer;
2737   vectors[0].size = 5;
2738
2739   vectors[1].buffer = buffer + 5;
2740   vectors[1].size = 12;
2741
2742   vectors[2].buffer = buffer + 5  + 12;
2743   vectors[2].size = 3;
2744
2745   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2746                          &iostream, NULL);
2747   g_assert_nonnull (file);
2748   g_assert_nonnull (iostream);
2749
2750   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2751
2752   cancellable = g_cancellable_new ();
2753   g_cancellable_cancel (cancellable);
2754
2755   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, cancellable, test_writev_all_cb, &data);
2756
2757   while (!data.done)
2758     g_main_context_iteration (NULL, TRUE);
2759
2760   g_assert_cmpuint (data.bytes_written, ==, 0);
2761   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
2762   g_clear_error (&data.error);
2763
2764   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2765   g_assert_no_error (error);
2766   g_assert_true (res);
2767   g_object_unref (iostream);
2768
2769   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2770   g_assert_no_error (error);
2771   g_assert_true (res);
2772   g_assert_cmpuint (length, ==, 0);
2773
2774   g_free (contents);
2775
2776   g_file_delete (file, NULL, NULL);
2777   g_object_unref (file);
2778   g_object_unref (cancellable);
2779 }
2780
2781 /* Test that writev_async_all() with empty vectors is handled correctly */
2782 static void
2783 test_writev_async_all_empty_vectors (void)
2784 {
2785   WritevAsyncData data = { 0 };
2786   GFile *file;
2787   GFileIOStream *iostream = NULL;
2788   GOutputVector vectors[3];
2789   GOutputStream *ostream;
2790   GError *error = NULL;
2791   gboolean res;
2792   guint8 *contents;
2793   gsize length;
2794
2795   vectors[0].buffer = NULL;
2796   vectors[0].size = 0;
2797
2798   vectors[1].buffer = NULL;
2799   vectors[1].size = 0;
2800
2801   vectors[2].buffer = NULL;
2802   vectors[2].size = 0;
2803
2804   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2805                          &iostream, NULL);
2806   g_assert_nonnull (file);
2807   g_assert_nonnull (iostream);
2808
2809   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2810
2811   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data);
2812
2813   while (!data.done)
2814     g_main_context_iteration (NULL, TRUE);
2815
2816   g_assert_cmpuint (data.bytes_written, ==, 0);
2817   g_assert_no_error (data.error);
2818   g_clear_error (&data.error);
2819
2820   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2821   g_assert_no_error (error);
2822   g_assert_true (res);
2823   g_object_unref (iostream);
2824
2825   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2826   g_assert_no_error (error);
2827   g_assert_true (res);
2828   g_assert_cmpuint (length, ==, 0);
2829
2830   g_free (contents);
2831
2832   g_file_delete (file, NULL, NULL);
2833   g_object_unref (file);
2834 }
2835
2836 /* Test that writev_async_all() with no vectors is handled correctly */
2837 static void
2838 test_writev_async_all_no_vectors (void)
2839 {
2840   WritevAsyncData data = { 0 };
2841   GFile *file;
2842   GFileIOStream *iostream = NULL;
2843   GOutputStream *ostream;
2844   GError *error = NULL;
2845   gboolean res;
2846   guint8 *contents;
2847   gsize length;
2848
2849   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2850                          &iostream, NULL);
2851   g_assert_nonnull (file);
2852   g_assert_nonnull (iostream);
2853
2854   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2855
2856   g_output_stream_writev_all_async (ostream, NULL, 0, 0, NULL, test_writev_all_cb, &data);
2857
2858   while (!data.done)
2859     g_main_context_iteration (NULL, TRUE);
2860
2861   g_assert_cmpuint (data.bytes_written, ==, 0);
2862   g_assert_no_error (data.error);
2863   g_clear_error (&data.error);
2864
2865   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2866   g_assert_no_error (error);
2867   g_assert_true (res);
2868   g_object_unref (iostream);
2869
2870   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2871   g_assert_no_error (error);
2872   g_assert_true (res);
2873   g_assert_cmpuint (length, ==, 0);
2874
2875   g_free (contents);
2876
2877   g_file_delete (file, NULL, NULL);
2878   g_object_unref (file);
2879 }
2880
2881 /* Test that writev_async_all() with too big vectors is handled correctly */
2882 static void
2883 test_writev_async_all_too_big_vectors (void)
2884 {
2885   WritevAsyncData data = { 0 };
2886   GFile *file;
2887   GFileIOStream *iostream = NULL;
2888   GOutputVector vectors[3];
2889   GOutputStream *ostream;
2890   GError *error = NULL;
2891   gboolean res;
2892   guint8 *contents;
2893   gsize length;
2894
2895   vectors[0].buffer = (void*) 1;
2896   vectors[0].size = G_MAXSIZE / 2;
2897
2898   vectors[1].buffer = (void*) 1;
2899   vectors[1].size = G_MAXSIZE / 2;
2900
2901   vectors[2].buffer = (void*) 1;
2902   vectors[2].size = G_MAXSIZE / 2;
2903
2904   file = g_file_new_tmp ("g_file_writev_XXXXXX",
2905                          &iostream, NULL);
2906   g_assert_nonnull (file);
2907   g_assert_nonnull (iostream);
2908
2909   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
2910
2911   g_output_stream_writev_all_async (ostream, vectors, G_N_ELEMENTS (vectors), 0, NULL, test_writev_all_cb, &data);
2912
2913   while (!data.done)
2914     g_main_context_iteration (NULL, TRUE);
2915
2916   g_assert_cmpuint (data.bytes_written, ==, 0);
2917   g_assert_error (data.error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
2918   g_clear_error (&data.error);
2919
2920   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
2921   g_assert_no_error (error);
2922   g_assert_true (res);
2923   g_object_unref (iostream);
2924
2925   res = g_file_load_contents (file, NULL, (gchar **) &contents, &length, NULL, &error);
2926   g_assert_no_error (error);
2927   g_assert_true (res);
2928   g_assert_cmpuint (length, ==, 0);
2929
2930   g_free (contents);
2931
2932   g_file_delete (file, NULL, NULL);
2933   g_object_unref (file);
2934 }
2935
2936 static void
2937 test_build_attribute_list_for_copy (void)
2938 {
2939   GFile *tmpfile;
2940   GFileIOStream *iostream;
2941   GError *error = NULL;
2942   const GFileCopyFlags test_flags[] =
2943     {
2944       G_FILE_COPY_NONE,
2945       G_FILE_COPY_TARGET_DEFAULT_PERMS,
2946       G_FILE_COPY_ALL_METADATA,
2947       G_FILE_COPY_ALL_METADATA | G_FILE_COPY_TARGET_DEFAULT_PERMS,
2948     };
2949   gsize i;
2950   char *attrs;
2951   gchar *attrs_with_commas;
2952
2953   tmpfile = g_file_new_tmp ("tmp-build-attribute-list-for-copyXXXXXX",
2954                             &iostream, &error);
2955   g_assert_no_error (error);
2956   g_io_stream_close ((GIOStream*)iostream, NULL, &error);
2957   g_assert_no_error (error);
2958   g_clear_object (&iostream);
2959
2960   for (i = 0; i < G_N_ELEMENTS (test_flags); i++)
2961     {
2962       GFileCopyFlags flags = test_flags[i];
2963
2964       attrs = g_file_build_attribute_list_for_copy (tmpfile, flags, NULL, &error);
2965       g_test_message ("Attributes for copy: %s", attrs);
2966       g_assert_no_error (error);
2967       g_assert_nonnull (attrs);
2968       attrs_with_commas = g_strconcat (",", attrs, ",", NULL);
2969       g_free (attrs);
2970
2971       /* See g_local_file_class_init for reference. */
2972       if (flags & G_FILE_COPY_TARGET_DEFAULT_PERMS)
2973         g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ","));
2974       else
2975         g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_MODE ","));
2976 #ifdef G_OS_UNIX
2977       if (flags & G_FILE_COPY_ALL_METADATA)
2978         {
2979           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ","));
2980           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ","));
2981         }
2982       else
2983         {
2984           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_UID ","));
2985           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_UNIX_GID ","));
2986         }
2987 #endif
2988 #ifdef HAVE_UTIMES
2989       g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED ","));
2990       g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC ","));
2991       if (flags & G_FILE_COPY_ALL_METADATA)
2992         {
2993           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
2994           g_assert_nonnull (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
2995         }
2996       else
2997         {
2998           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS ","));
2999           g_assert_null (g_strstr_len (attrs_with_commas, -1, "," G_FILE_ATTRIBUTE_TIME_ACCESS_USEC ","));
3000         }
3001 #endif
3002       g_free (attrs_with_commas);
3003     }
3004
3005   (void) g_file_delete (tmpfile, NULL, NULL);
3006   g_clear_object (&tmpfile);
3007 }
3008
3009 typedef struct
3010 {
3011   GError *error;
3012   gboolean done;
3013   gboolean res;
3014 } MoveAsyncData;
3015
3016 static void
3017 test_move_async_cb (GObject      *object,
3018                     GAsyncResult *result,
3019                     gpointer      user_data)
3020 {
3021   GFile *file = G_FILE (object);
3022   MoveAsyncData *data = user_data;
3023   GError *error = NULL;
3024
3025   data->res = g_file_move_finish (file, result, &error);
3026   data->error = error;
3027   data->done = TRUE;
3028 }
3029
3030 typedef struct
3031 {
3032   goffset total_num_bytes;
3033 } MoveAsyncProgressData;
3034
3035 static void
3036 test_move_async_progress_cb (goffset  current_num_bytes,
3037                              goffset  total_num_bytes,
3038                              gpointer user_data)
3039 {
3040   MoveAsyncProgressData *data = user_data;
3041   data->total_num_bytes = total_num_bytes;
3042 }
3043
3044 /* Test that move_async() moves the file correctly */
3045 static void
3046 test_move_async (void)
3047 {
3048   MoveAsyncData data = { 0 };
3049   MoveAsyncProgressData progress_data = { 0 };
3050   GFile *source;
3051   GFileIOStream *iostream;
3052   GOutputStream *ostream;
3053   GFile *destination;
3054   gchar *destination_path;
3055   GError *error = NULL;
3056   gboolean res;
3057   const guint8 buffer[] = {1, 2, 3, 4, 5};
3058
3059   source = g_file_new_tmp ("g_file_move_XXXXXX", &iostream, NULL);
3060
3061   destination_path = g_build_path (G_DIR_SEPARATOR_S, g_get_tmp_dir (), "g_file_move_target", NULL);
3062   destination = g_file_new_for_path (destination_path);
3063
3064   g_assert_nonnull (source);
3065   g_assert_nonnull (iostream);
3066
3067   res = g_file_query_exists (source, NULL);
3068   g_assert_true (res);
3069   res = g_file_query_exists (destination, NULL);
3070   g_assert_false (res);
3071
3072   // Write a known amount of bytes to the file, so we can test the progress callback against it
3073   ostream = g_io_stream_get_output_stream (G_IO_STREAM (iostream));
3074   g_output_stream_write (ostream, buffer, sizeof (buffer), NULL, &error);
3075   g_assert_no_error (error);
3076
3077   g_file_move_async (source,
3078                      destination,
3079                      G_FILE_COPY_NONE,
3080                      0,
3081                      NULL,
3082                      test_move_async_progress_cb,
3083                      &progress_data,
3084                      test_move_async_cb,
3085                      &data);
3086
3087   while (!data.done)
3088     g_main_context_iteration (NULL, TRUE);
3089
3090   g_assert_no_error (data.error);
3091   g_assert_true (data.res);
3092   g_assert_cmpuint (progress_data.total_num_bytes, ==, sizeof (buffer));
3093
3094   res = g_file_query_exists (source, NULL);
3095   g_assert_false (res);
3096   res = g_file_query_exists (destination, NULL);
3097   g_assert_true (res);
3098
3099   res = g_io_stream_close (G_IO_STREAM (iostream), NULL, &error);
3100   g_assert_no_error (error);
3101   g_assert_true (res);
3102   g_object_unref (iostream);
3103
3104   res = g_file_delete (destination, NULL, &error);
3105   g_assert_no_error (error);
3106   g_assert_true (res);
3107
3108   g_object_unref (source);
3109   g_object_unref (destination);
3110
3111   g_free (destination_path);
3112 }
3113
3114 int
3115 main (int argc, char *argv[])
3116 {
3117   setlocale (LC_ALL, "");
3118
3119   g_test_init (&argc, &argv, NULL);
3120
3121   g_test_add_func ("/file/basic", test_basic);
3122   g_test_add_func ("/file/build-filename", test_build_filename);
3123   g_test_add_func ("/file/parent", test_parent);
3124   g_test_add_func ("/file/child", test_child);
3125   g_test_add_func ("/file/empty-path", test_empty_path);
3126   g_test_add_func ("/file/type", test_type);
3127   g_test_add_func ("/file/parse-name", test_parse_name);
3128   g_test_add_data_func ("/file/async-create-delete/0", GINT_TO_POINTER (0), test_create_delete);
3129   g_test_add_data_func ("/file/async-create-delete/1", GINT_TO_POINTER (1), test_create_delete);
3130   g_test_add_data_func ("/file/async-create-delete/10", GINT_TO_POINTER (10), test_create_delete);
3131   g_test_add_data_func ("/file/async-create-delete/25", GINT_TO_POINTER (25), test_create_delete);
3132   g_test_add_data_func ("/file/async-create-delete/4096", GINT_TO_POINTER (4096), test_create_delete);
3133   g_test_add_func ("/file/replace-load", test_replace_load);
3134   g_test_add_func ("/file/replace-cancel", test_replace_cancel);
3135   g_test_add_func ("/file/replace-symlink", test_replace_symlink);
3136   g_test_add_func ("/file/replace-symlink/using-etag", test_replace_symlink_using_etag);
3137   g_test_add_data_func ("/file/replace/write-only", GUINT_TO_POINTER (FALSE), test_replace);
3138   g_test_add_data_func ("/file/replace/read-write", GUINT_TO_POINTER (TRUE), test_replace);
3139   g_test_add_func ("/file/async-delete", test_async_delete);
3140   g_test_add_func ("/file/copy-preserve-mode", test_copy_preserve_mode);
3141   g_test_add_func ("/file/measure", test_measure);
3142   g_test_add_func ("/file/measure-async", test_measure_async);
3143   g_test_add_func ("/file/load-bytes", test_load_bytes);
3144   g_test_add_func ("/file/load-bytes-async", test_load_bytes_async);
3145   g_test_add_func ("/file/writev", test_writev);
3146   g_test_add_func ("/file/writev/no-bytes-written", test_writev_no_bytes_written);
3147   g_test_add_func ("/file/writev/no-vectors", test_writev_no_vectors);
3148   g_test_add_func ("/file/writev/empty-vectors", test_writev_empty_vectors);
3149   g_test_add_func ("/file/writev/too-big-vectors", test_writev_too_big_vectors);
3150   g_test_add_func ("/file/writev/async", test_writev_async);
3151   g_test_add_func ("/file/writev/async_all", test_writev_async_all);
3152   g_test_add_func ("/file/writev/async_all-empty-vectors", test_writev_async_all_empty_vectors);
3153   g_test_add_func ("/file/writev/async_all-no-vectors", test_writev_async_all_no_vectors);
3154   g_test_add_func ("/file/writev/async_all-to-big-vectors", test_writev_async_all_too_big_vectors);
3155   g_test_add_func ("/file/writev/async_all-cancellation", test_writev_async_all_cancellation);
3156   g_test_add_func ("/file/build-attribute-list-for-copy", test_build_attribute_list_for_copy);
3157   g_test_add_func ("/file/move_async", test_move_async);
3158
3159   return g_test_run ();
3160 }