Meson: Group all glib tests into a single dict
[glib.git] / gio / tests / testfilemonitor.c
blob0fa72d3d93dd949c24b79bd04bb867e47f97486d
1 #include "config.h"
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <gio/gio.h>
7 /* These tests were written for the inotify implementation.
8 * Other implementations may require slight adjustments in
9 * the tests, e.g. the length of timeouts
12 typedef enum {
13 NONE = 0,
14 INOTIFY = (1 << 1),
15 KQUEUE = (1 << 2)
16 } Environment;
18 typedef struct
20 gint event_type;
21 gchar *file;
22 gchar *other_file;
23 gint step;
25 /* Since different file monitor implementation has different capabilities,
26 * we cannot expect all implementations to report all kind of events without
27 * any loss. This 'optional' field is a bit mask used to mark events which
28 * may be lost under specific platforms.
30 Environment optional;
31 } RecordedEvent;
33 static void
34 free_recorded_event (RecordedEvent *event)
36 g_free (event->file);
37 g_free (event->other_file);
38 g_free (event);
41 typedef struct
43 GFile *file;
44 GFileMonitor *monitor;
45 GMainLoop *loop;
46 gint step;
47 GList *events;
48 GFileOutputStream *output_stream;
49 } TestData;
51 #if 0
52 static void
53 output_event (RecordedEvent *event)
55 if (event->step >= 0)
56 g_print (">>>> step %d\n", event->step);
57 else
59 GTypeClass *class;
61 class = g_type_class_ref (g_type_from_name ("GFileMonitorEvent"));
62 g_print ("%s file=%s other_file=%s\n",
63 g_enum_get_value (G_ENUM_CLASS (class), event->event_type)->value_nick,
64 event->file,
65 event->other_file);
66 g_type_class_unref (class);
70 static void
71 output_events (GList *list)
73 GList *l;
75 g_print (">>>output events\n");
76 for (l = list; l; l = l->next)
77 output_event ((RecordedEvent *)l->data);
79 #endif
81 /* a placeholder for temp file names we don't want to compare */
82 static const gchar DONT_CARE[] = "";
84 static Environment
85 get_environment (GFileMonitor *monitor)
87 if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GInotifyFileMonitor"))
88 return INOTIFY;
89 if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GKqueueFileMonitor"))
90 return KQUEUE;
91 return NONE;
94 static void
95 check_expected_events (RecordedEvent *expected,
96 gsize n_expected,
97 GList *recorded,
98 Environment env)
100 gint i, li;
101 GList *l;
103 for (i = 0, li = 0, l = recorded; i < n_expected && l != NULL;)
105 RecordedEvent *e1 = &expected[i];
106 RecordedEvent *e2 = l->data;
107 gboolean mismatch = TRUE;
108 gboolean l_extra_step = FALSE;
112 gboolean ignore_other_file = FALSE;
114 if (e1->step != e2->step)
115 break;
117 /* Kqueue isn't good at detecting file renaming, so
118 * G_FILE_MONITOR_WATCH_MOVES is mostly useless there. */
119 if (e1->event_type != e2->event_type && env & KQUEUE)
121 /* It is possible for kqueue file monitor to emit 'RENAMED' event,
122 * but most of the time it is reported as a 'DELETED' event and
123 * a 'CREATED' event. */
124 if (e1->event_type == G_FILE_MONITOR_EVENT_RENAMED)
126 RecordedEvent *e2_next;
128 if (l->next == NULL)
129 break;
130 e2_next = l->next->data;
132 if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
133 break;
134 if (e2_next->event_type != G_FILE_MONITOR_EVENT_CREATED)
135 break;
137 if (e1->step != e2_next->step)
138 break;
140 if (e1->file != DONT_CARE &&
141 (g_strcmp0 (e1->file, e2->file) != 0 ||
142 e2->other_file != NULL))
143 break;
145 if (e1->other_file != DONT_CARE &&
146 (g_strcmp0 (e1->other_file, e2_next->file) != 0 ||
147 e2_next->other_file != NULL))
148 break;
150 l_extra_step = TRUE;
151 mismatch = FALSE;
152 break;
154 /* Kqueue won't report 'MOVED_IN' and 'MOVED_OUT' events. We set
155 * 'ignore_other_file' here to let the following code know that
156 * 'other_file' may not match. */
157 else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_IN)
159 if (e2->event_type != G_FILE_MONITOR_EVENT_CREATED)
160 break;
161 ignore_other_file = TRUE;
163 else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_OUT)
165 if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
166 break;
167 ignore_other_file = TRUE;
169 else
170 break;
173 if (e1->file != DONT_CARE &&
174 g_strcmp0 (e1->file, e2->file) != 0)
175 break;
177 if (e1->other_file != DONT_CARE && !ignore_other_file &&
178 g_strcmp0 (e1->other_file, e2->other_file) != 0)
179 break;
181 mismatch = FALSE;
183 while (0);
185 if (mismatch)
187 /* Sometimes the emission of 'CHANGES_DONE_HINT' may be late because
188 * it depends on the ability of file monitor implementation to report
189 * 'CHANGES_DONE_HINT' itself. If the file monitor implementation
190 * doesn't report 'CHANGES_DONE_HINT' itself, it may be emitted by
191 * GLocalFileMonitor after a few seconds, which causes the event to
192 * mix with results from different steps. Since 'CHANGES_DONE_HINT'
193 * is just a hint, we don't require it to be reliable and we simply
194 * ignore unexpected 'CHANGES_DONE_HINT' events here. */
195 if (e1->event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
196 e2->event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
198 g_test_message ("Event CHANGES_DONE_HINT ignored at "
199 "expected index %d, recorded index %d", i, li);
200 li++, l = l->next;
201 continue;
203 /* If an event is marked as optional in the current environment and
204 * the event doesn't match, it means the expected event has lost. */
205 else if (env & e1->optional)
207 g_test_message ("Event %d at expected index %d skipped because "
208 "it is marked as optional", e1->event_type, i);
209 i++;
210 continue;
212 /* Run above checks under g_assert_* again to provide more useful
213 * error messages. */
214 else
216 g_assert_cmpint (e1->step, ==, e2->step);
217 g_assert_cmpint (e1->event_type, ==, e2->event_type);
219 if (e1->file != DONT_CARE)
220 g_assert_cmpstr (e1->file, ==, e2->file);
222 if (e1->other_file != DONT_CARE)
223 g_assert_cmpstr (e1->other_file, ==, e2->other_file);
225 g_assert_not_reached ();
229 i++, li++, l = l->next;
230 if (l_extra_step)
231 li++, l = l->next;
234 g_assert_cmpint (i, ==, n_expected);
235 g_assert_cmpint (li, ==, g_list_length (recorded));
238 static void
239 record_event (TestData *data,
240 gint event_type,
241 const gchar *file,
242 const gchar *other_file,
243 gint step)
245 RecordedEvent *event;
247 event = g_new0 (RecordedEvent, 1);
248 event->event_type = event_type;
249 event->file = g_strdup (file);
250 event->other_file = g_strdup (other_file);
251 event->step = step;
253 data->events = g_list_append (data->events, event);
256 static void
257 monitor_changed (GFileMonitor *monitor,
258 GFile *file,
259 GFile *other_file,
260 GFileMonitorEvent event_type,
261 gpointer user_data)
263 TestData *data = user_data;
264 gchar *basename, *other_base;
266 basename = g_file_get_basename (file);
267 if (other_file)
268 other_base = g_file_get_basename (other_file);
269 else
270 other_base = NULL;
272 record_event (data, event_type, basename, other_base, -1);
274 g_free (basename);
275 g_free (other_base);
278 static gboolean
279 atomic_replace_step (gpointer user_data)
281 TestData *data = user_data;
282 GError *error = NULL;
284 switch (data->step)
286 case 0:
287 record_event (data, -1, NULL, NULL, 0);
288 g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
289 g_assert_no_error (error);
290 break;
291 case 1:
292 record_event (data, -1, NULL, NULL, 1);
293 g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
294 g_assert_no_error (error);
295 break;
296 case 2:
297 record_event (data, -1, NULL, NULL, 2);
298 g_file_delete (data->file, NULL, NULL);
299 break;
300 case 3:
301 record_event (data, -1, NULL, NULL, 3);
302 g_main_loop_quit (data->loop);
303 return G_SOURCE_REMOVE;
306 data->step++;
308 return G_SOURCE_CONTINUE;
311 /* this is the output we expect from the above steps */
312 static RecordedEvent atomic_replace_output[] = {
313 { -1, NULL, NULL, 0, NONE },
314 { G_FILE_MONITOR_EVENT_CREATED, "atomic_replace_file", NULL, -1, NONE },
315 { G_FILE_MONITOR_EVENT_CHANGED, "atomic_replace_file", NULL, -1, KQUEUE },
316 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "atomic_replace_file", NULL, -1, KQUEUE },
317 { -1, NULL, NULL, 1, NONE },
318 { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE, "atomic_replace_file", -1, NONE },
319 { -1, NULL, NULL, 2, NONE },
320 { G_FILE_MONITOR_EVENT_DELETED, "atomic_replace_file", NULL, -1, NONE },
321 { -1, NULL, NULL, 3, NONE }
324 static void
325 test_atomic_replace (void)
327 GError *error = NULL;
328 TestData data;
330 data.step = 0;
331 data.events = NULL;
333 data.file = g_file_new_for_path ("atomic_replace_file");
334 g_file_delete (data.file, NULL, NULL);
336 data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
337 g_assert_no_error (error);
339 g_file_monitor_set_rate_limit (data.monitor, 200);
340 g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
342 data.loop = g_main_loop_new (NULL, TRUE);
344 g_timeout_add (500, atomic_replace_step, &data);
346 g_main_loop_run (data.loop);
348 /*output_events (data.events);*/
349 check_expected_events (atomic_replace_output,
350 G_N_ELEMENTS (atomic_replace_output),
351 data.events,
352 get_environment (data.monitor));
354 g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
355 g_main_loop_unref (data.loop);
356 g_object_unref (data.monitor);
357 g_object_unref (data.file);
360 static gboolean
361 change_step (gpointer user_data)
363 TestData *data = user_data;
364 GOutputStream *stream;
365 GError *error = NULL;
366 guint32 mode = 0660;
368 switch (data->step)
370 case 0:
371 record_event (data, -1, NULL, NULL, 0);
372 g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
373 g_assert_no_error (error);
374 break;
375 case 1:
376 record_event (data, -1, NULL, NULL, 1);
377 stream = (GOutputStream *)g_file_append_to (data->file, G_FILE_CREATE_NONE, NULL, &error);
378 g_assert_no_error (error);
379 g_output_stream_write_all (stream, " step 1", 7, NULL, NULL, &error);
380 g_assert_no_error (error);
381 g_output_stream_close (stream, NULL, &error);
382 g_assert_no_error (error);
383 g_object_unref (stream);
384 break;
385 case 2:
386 record_event (data, -1, NULL, NULL, 2);
387 g_file_set_attribute (data->file,
388 G_FILE_ATTRIBUTE_UNIX_MODE,
389 G_FILE_ATTRIBUTE_TYPE_UINT32,
390 &mode,
391 G_FILE_QUERY_INFO_NONE,
392 NULL,
393 &error);
394 g_assert_no_error (error);
395 break;
396 case 3:
397 record_event (data, -1, NULL, NULL, 3);
398 g_file_delete (data->file, NULL, NULL);
399 break;
400 case 4:
401 record_event (data, -1, NULL, NULL, 4);
402 g_main_loop_quit (data->loop);
403 return G_SOURCE_REMOVE;
406 data->step++;
408 return G_SOURCE_CONTINUE;
411 /* this is the output we expect from the above steps */
412 static RecordedEvent change_output[] = {
413 { -1, NULL, NULL, 0, NONE },
414 { G_FILE_MONITOR_EVENT_CREATED, "change_file", NULL, -1, NONE },
415 { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, KQUEUE },
416 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, KQUEUE },
417 { -1, NULL, NULL, 1, NONE },
418 { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, NONE },
419 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, NONE },
420 { -1, NULL, NULL, 2, NONE },
421 { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, "change_file", NULL, -1, NONE },
422 { -1, NULL, NULL, 3, NONE },
423 { G_FILE_MONITOR_EVENT_DELETED, "change_file", NULL, -1, NONE },
424 { -1, NULL, NULL, 4, NONE }
427 static void
428 test_file_changes (void)
430 GError *error = NULL;
431 TestData data;
433 data.step = 0;
434 data.events = NULL;
436 data.file = g_file_new_for_path ("change_file");
437 g_file_delete (data.file, NULL, NULL);
439 data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
440 g_assert_no_error (error);
442 g_file_monitor_set_rate_limit (data.monitor, 200);
443 g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
445 data.loop = g_main_loop_new (NULL, TRUE);
447 g_timeout_add (500, change_step, &data);
449 g_main_loop_run (data.loop);
451 /*output_events (data.events);*/
452 check_expected_events (change_output,
453 G_N_ELEMENTS (change_output),
454 data.events,
455 get_environment (data.monitor));
457 g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
458 g_main_loop_unref (data.loop);
459 g_object_unref (data.monitor);
460 g_object_unref (data.file);
463 static gboolean
464 dir_step (gpointer user_data)
466 TestData *data = user_data;
467 GFile *parent, *file, *file2;
468 GError *error = NULL;
470 switch (data->step)
472 case 1:
473 record_event (data, -1, NULL, NULL, 1);
474 parent = g_file_get_parent (data->file);
475 file = g_file_get_child (parent, "dir_test_file");
476 g_file_replace_contents (file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
477 g_assert_no_error (error);
478 g_object_unref (file);
479 g_object_unref (parent);
480 break;
481 case 2:
482 record_event (data, -1, NULL, NULL, 2);
483 parent = g_file_get_parent (data->file);
484 file = g_file_get_child (parent, "dir_test_file");
485 file2 = g_file_get_child (data->file, "dir_test_file");
486 g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
487 g_assert_no_error (error);
488 g_object_unref (file);
489 g_object_unref (file2);
490 g_object_unref (parent);
491 break;
492 case 3:
493 record_event (data, -1, NULL, NULL, 3);
494 file = g_file_get_child (data->file, "dir_test_file");
495 file2 = g_file_get_child (data->file, "dir_test_file2");
496 g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
497 g_assert_no_error (error);
498 g_object_unref (file);
499 g_object_unref (file2);
500 break;
501 case 4:
502 record_event (data, -1, NULL, NULL, 4);
503 parent = g_file_get_parent (data->file);
504 file = g_file_get_child (data->file, "dir_test_file2");
505 file2 = g_file_get_child (parent, "dir_test_file2");
506 g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
507 g_assert_no_error (error);
508 g_file_delete (file2, NULL, NULL);
509 g_object_unref (file);
510 g_object_unref (file2);
511 g_object_unref (parent);
512 break;
513 case 5:
514 record_event (data, -1, NULL, NULL, 5);
515 g_file_delete (data->file, NULL, NULL);
516 break;
517 case 6:
518 record_event (data, -1, NULL, NULL, 6);
519 g_main_loop_quit (data->loop);
520 return G_SOURCE_REMOVE;
523 data->step++;
525 return G_SOURCE_CONTINUE;
528 /* this is the output we expect from the above steps */
529 static RecordedEvent dir_output[] = {
530 { -1, NULL, NULL, 1, NONE },
531 { -1, NULL, NULL, 2, NONE },
532 { G_FILE_MONITOR_EVENT_MOVED_IN, "dir_test_file", NULL, -1, NONE },
533 { -1, NULL, NULL, 3, NONE },
534 { G_FILE_MONITOR_EVENT_RENAMED, "dir_test_file", "dir_test_file2", -1, NONE },
535 { -1, NULL, NULL, 4, NONE },
536 { G_FILE_MONITOR_EVENT_MOVED_OUT, "dir_test_file2", NULL, -1, NONE },
537 { -1, NULL, NULL, 5, NONE },
538 { G_FILE_MONITOR_EVENT_DELETED, "dir_monitor_test", NULL, -1, NONE },
539 { -1, NULL, NULL, 6, NONE }
542 static void
543 test_dir_monitor (void)
545 GError *error = NULL;
546 TestData data;
548 data.step = 0;
549 data.events = NULL;
551 data.file = g_file_new_for_path ("dir_monitor_test");
552 g_file_delete (data.file, NULL, NULL);
553 g_file_make_directory (data.file, NULL, &error);
555 data.monitor = g_file_monitor_directory (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
556 g_assert_no_error (error);
558 g_file_monitor_set_rate_limit (data.monitor, 200);
559 g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
561 data.loop = g_main_loop_new (NULL, TRUE);
563 g_timeout_add (500, dir_step, &data);
565 g_main_loop_run (data.loop);
567 /*output_events (data.events);*/
568 check_expected_events (dir_output,
569 G_N_ELEMENTS (dir_output),
570 data.events,
571 get_environment (data.monitor));
573 g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
574 g_main_loop_unref (data.loop);
575 g_object_unref (data.monitor);
576 g_object_unref (data.file);
579 static gboolean
580 nodir_step (gpointer user_data)
582 TestData *data = user_data;
583 GFile *parent;
584 GError *error = NULL;
586 switch (data->step)
588 case 0:
589 record_event (data, -1, NULL, NULL, 0);
590 parent = g_file_get_parent (data->file);
591 g_file_make_directory (parent, NULL, &error);
592 g_assert_no_error (error);
593 g_object_unref (parent);
594 break;
595 case 1:
596 record_event (data, -1, NULL, NULL, 1);
597 g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
598 g_assert_no_error (error);
599 break;
600 case 2:
601 record_event (data, -1, NULL, NULL, 2);
602 g_file_delete (data->file, NULL, &error);
603 g_assert_no_error (error);
604 break;
605 case 3:
606 record_event (data, -1, NULL, NULL, 3);
607 parent = g_file_get_parent (data->file);
608 g_file_delete (parent, NULL, &error);
609 g_assert_no_error (error);
610 g_object_unref (parent);
611 break;
612 case 4:
613 record_event (data, -1, NULL, NULL, 4);
614 g_main_loop_quit (data->loop);
615 return G_SOURCE_REMOVE;
618 data->step++;
620 return G_SOURCE_CONTINUE;
623 static RecordedEvent nodir_output[] = {
624 { -1, NULL, NULL, 0, NONE },
625 { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, KQUEUE },
626 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
627 { -1, NULL, NULL, 1, NONE },
628 { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, NONE },
629 { G_FILE_MONITOR_EVENT_CHANGED, "nosuchfile", NULL, -1, KQUEUE },
630 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
631 { -1, NULL, NULL, 2, NONE },
632 { G_FILE_MONITOR_EVENT_DELETED, "nosuchfile", NULL, -1, NONE },
633 { -1, NULL, NULL, 3, NONE },
634 { -1, NULL, NULL, 4, NONE }
637 static void
638 test_dir_non_existent (void)
640 TestData data;
641 GError *error = NULL;
643 data.step = 0;
644 data.events = NULL;
646 data.file = g_file_new_for_path ("nosuchdir/nosuchfile");
647 data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
648 g_assert_no_error (error);
650 g_file_monitor_set_rate_limit (data.monitor, 200);
651 g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
653 data.loop = g_main_loop_new (NULL, TRUE);
655 /* we need a long timeout here, since the inotify implementation only scans
656 * for missing files every 4 seconds.
658 g_timeout_add (5000, nodir_step, &data);
660 g_main_loop_run (data.loop);
662 /*output_events (data.events);*/
663 check_expected_events (nodir_output,
664 G_N_ELEMENTS (nodir_output),
665 data.events,
666 get_environment (data.monitor));
668 g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
669 g_main_loop_unref (data.loop);
670 g_object_unref (data.monitor);
671 g_object_unref (data.file);
674 static gboolean
675 cross_dir_step (gpointer user_data)
677 TestData *data = user_data;
678 GFile *file, *file2;
679 GError *error = NULL;
681 switch (data[0].step)
683 case 0:
684 record_event (&data[0], -1, NULL, NULL, 0);
685 record_event (&data[1], -1, NULL, NULL, 0);
686 file = g_file_get_child (data[1].file, "a");
687 g_file_replace_contents (file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
688 g_assert_no_error (error);
689 g_object_unref (file);
690 break;
691 case 1:
692 record_event (&data[0], -1, NULL, NULL, 1);
693 record_event (&data[1], -1, NULL, NULL, 1);
694 file = g_file_get_child (data[1].file, "a");
695 file2 = g_file_get_child (data[0].file, "a");
696 g_file_move (file, file2, 0, NULL, NULL, NULL, &error);
697 g_assert_no_error (error);
698 g_object_unref (file);
699 g_object_unref (file2);
700 break;
701 case 2:
702 record_event (&data[0], -1, NULL, NULL, 2);
703 record_event (&data[1], -1, NULL, NULL, 2);
704 file2 = g_file_get_child (data[0].file, "a");
705 g_file_delete (file2, NULL, NULL);
706 g_file_delete (data[0].file, NULL, NULL);
707 g_file_delete (data[1].file, NULL, NULL);
708 g_object_unref (file2);
709 break;
710 case 3:
711 record_event (&data[0], -1, NULL, NULL, 3);
712 record_event (&data[1], -1, NULL, NULL, 3);
713 g_main_loop_quit (data->loop);
714 return G_SOURCE_REMOVE;
717 data->step++;
719 return G_SOURCE_CONTINUE;
722 static RecordedEvent cross_dir_a_output[] = {
723 { -1, NULL, NULL, 0, NONE },
724 { -1, NULL, NULL, 1, NONE },
725 { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
726 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
727 { -1, NULL, NULL, 2, NONE },
728 { G_FILE_MONITOR_EVENT_DELETED, "a", NULL, -1, NONE },
729 { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_a", NULL, -1, NONE },
730 { -1, NULL, NULL, 3, NONE },
733 static RecordedEvent cross_dir_b_output[] = {
734 { -1, NULL, NULL, 0, NONE },
735 { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
736 { G_FILE_MONITOR_EVENT_CHANGED, "a", NULL, -1, KQUEUE },
737 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
738 { -1, NULL, NULL, 1, NONE },
739 { G_FILE_MONITOR_EVENT_MOVED_OUT, "a", "a", -1, NONE },
740 { -1, NULL, NULL, 2, NONE },
741 { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_b", NULL, -1, NONE },
742 { -1, NULL, NULL, 3, NONE },
744 static void
745 test_cross_dir_moves (void)
747 GError *error = NULL;
748 TestData data[2];
750 data[0].step = 0;
751 data[0].events = NULL;
753 data[0].file = g_file_new_for_path ("cross_dir_a");
754 g_file_delete (data[0].file, NULL, NULL);
755 g_file_make_directory (data[0].file, NULL, &error);
757 data[0].monitor = g_file_monitor_directory (data[0].file, 0, NULL, &error);
758 g_assert_no_error (error);
760 g_file_monitor_set_rate_limit (data[0].monitor, 200);
761 g_signal_connect (data[0].monitor, "changed", G_CALLBACK (monitor_changed), &data[0]);
763 data[1].step = 0;
764 data[1].events = NULL;
766 data[1].file = g_file_new_for_path ("cross_dir_b");
767 g_file_delete (data[1].file, NULL, NULL);
768 g_file_make_directory (data[1].file, NULL, &error);
770 data[1].monitor = g_file_monitor_directory (data[1].file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
771 g_assert_no_error (error);
773 g_file_monitor_set_rate_limit (data[1].monitor, 200);
774 g_signal_connect (data[1].monitor, "changed", G_CALLBACK (monitor_changed), &data[1]);
776 data[0].loop = g_main_loop_new (NULL, TRUE);
778 g_timeout_add (500, cross_dir_step, data);
780 g_main_loop_run (data[0].loop);
782 #if 0
783 g_print ("monitor a:\n");
784 output_events (data[0].events);
785 g_print ("monitor b:\n");
786 output_events (data[1].events);
787 #endif
789 check_expected_events (cross_dir_a_output,
790 G_N_ELEMENTS (cross_dir_a_output),
791 data[0].events,
792 get_environment (data[0].monitor));
793 check_expected_events (cross_dir_b_output,
794 G_N_ELEMENTS (cross_dir_b_output),
795 data[1].events,
796 get_environment (data[1].monitor));
798 g_list_free_full (data[0].events, (GDestroyNotify)free_recorded_event);
799 g_main_loop_unref (data[0].loop);
800 g_object_unref (data[0].monitor);
801 g_object_unref (data[0].file);
803 g_list_free_full (data[1].events, (GDestroyNotify)free_recorded_event);
804 g_object_unref (data[1].monitor);
805 g_object_unref (data[1].file);
808 static gboolean
809 file_hard_links_step (gpointer user_data)
811 gboolean retval = G_SOURCE_CONTINUE;
812 TestData *data = user_data;
813 GError *error = NULL;
815 gchar *filename = g_file_get_path (data->file);
816 gchar *hard_link_name = g_strdup_printf ("%s2", filename);
817 GFile *hard_link_file = g_file_new_for_path (hard_link_name);
819 switch (data->step)
821 case 0:
822 record_event (data, -1, NULL, NULL, 0);
823 g_output_stream_write_all (G_OUTPUT_STREAM (data->output_stream),
824 "hello, step 0", 13, NULL, NULL, &error);
825 g_assert_no_error (error);
826 g_output_stream_close (G_OUTPUT_STREAM (data->output_stream), NULL, &error);
827 g_assert_no_error (error);
828 break;
829 case 1:
830 record_event (data, -1, NULL, NULL, 1);
831 g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE,
832 G_FILE_CREATE_NONE, NULL, NULL, &error);
833 g_assert_no_error (error);
834 break;
835 case 2:
836 record_event (data, -1, NULL, NULL, 2);
837 #ifdef HAVE_LINK
838 if (link (filename, hard_link_name) < 0)
840 g_error ("link(%s, %s) failed: %s", filename, hard_link_name, g_strerror (errno));
842 #endif /* HAVE_LINK */
843 break;
844 case 3:
845 record_event (data, -1, NULL, NULL, 3);
846 #ifdef HAVE_LINK
848 GOutputStream *hard_link_stream = NULL;
850 /* Deliberately don’t do an atomic swap on the hard-linked file. */
851 hard_link_stream = G_OUTPUT_STREAM (g_file_append_to (hard_link_file,
852 G_FILE_CREATE_NONE,
853 NULL, &error));
854 g_assert_no_error (error);
855 g_output_stream_write_all (hard_link_stream, " step 3", 7, NULL, NULL, &error);
856 g_assert_no_error (error);
857 g_output_stream_close (hard_link_stream, NULL, &error);
858 g_assert_no_error (error);
859 g_object_unref (hard_link_stream);
861 #endif /* HAVE_LINK */
862 break;
863 case 4:
864 record_event (data, -1, NULL, NULL, 4);
865 g_file_delete (data->file, NULL, &error);
866 g_assert_no_error (error);
867 break;
868 case 5:
869 record_event (data, -1, NULL, NULL, 5);
870 #ifdef HAVE_LINK
871 g_file_delete (hard_link_file, NULL, &error);
872 g_assert_no_error (error);
873 #endif /* HAVE_LINK */
874 break;
875 case 6:
876 record_event (data, -1, NULL, NULL, 6);
877 g_main_loop_quit (data->loop);
878 retval = G_SOURCE_REMOVE;
879 break;
882 if (retval != G_SOURCE_REMOVE)
883 data->step++;
885 g_object_unref (hard_link_file);
886 g_free (hard_link_name);
887 g_free (filename);
889 return retval;
892 static RecordedEvent file_hard_links_output[] = {
893 { -1, NULL, NULL, 0, NONE },
894 { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, NONE },
895 { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "testfilemonitor.db", NULL, -1, NONE },
896 { -1, NULL, NULL, 1, NONE },
897 { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE /* .goutputstream-XXXXXX */, "testfilemonitor.db", -1, NONE },
898 { -1, NULL, NULL, 2, NONE },
899 { -1, NULL, NULL, 3, NONE },
900 /* Kqueue is based on file descriptors. You can get events from all hard
901 * links by just monitoring one open file descriptor, and it is not possible
902 * to know whether it is done on the file name we use to open the file. Since
903 * the hard link count of 'testfilemonitor.db' is 2, it is expected to see
904 * two 'DELETED' events reported here. You have to call 'unlink' twice on
905 * different file names to remove 'testfilemonitor.db' from the file system,
906 * and each 'unlink' call generates a 'DELETED' event. */
907 { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, INOTIFY },
908 { -1, NULL, NULL, 4, NONE },
909 { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, NONE },
910 { -1, NULL, NULL, 5, NONE },
911 { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, INOTIFY },
912 { -1, NULL, NULL, 6, NONE },
915 static void
916 test_file_hard_links (void)
918 GError *error = NULL;
919 TestData data;
921 g_test_bug ("755721");
923 #ifdef HAVE_LINK
924 g_test_message ("Running with hard link tests");
925 #else /* if !HAVE_LINK */
926 g_test_message ("Running without hard link tests");
927 #endif /* !HAVE_LINK */
929 data.step = 0;
930 data.events = NULL;
932 /* Create a file which exists and is not a directory. */
933 data.file = g_file_new_for_path ("testfilemonitor.db");
934 data.output_stream = g_file_replace (data.file, NULL, FALSE,
935 G_FILE_CREATE_NONE, NULL, &error);
936 g_assert_no_error (error);
938 /* Monitor it. Creating the monitor should not crash (bug #755721). */
939 data.monitor = g_file_monitor_file (data.file,
940 G_FILE_MONITOR_WATCH_MOUNTS |
941 G_FILE_MONITOR_WATCH_MOVES |
942 G_FILE_MONITOR_WATCH_HARD_LINKS,
943 NULL,
944 &error);
945 g_assert_no_error (error);
946 g_assert_nonnull (data.monitor);
948 /* Change the file a bit. */
949 g_file_monitor_set_rate_limit (data.monitor, 200);
950 g_signal_connect (data.monitor, "changed", (GCallback) monitor_changed, &data);
952 data.loop = g_main_loop_new (NULL, TRUE);
953 g_timeout_add (500, file_hard_links_step, &data);
954 g_main_loop_run (data.loop);
956 /* output_events (data.events); */
957 check_expected_events (file_hard_links_output,
958 G_N_ELEMENTS (file_hard_links_output),
959 data.events,
960 get_environment (data.monitor));
962 g_list_free_full (data.events, (GDestroyNotify) free_recorded_event);
963 g_main_loop_unref (data.loop);
964 g_object_unref (data.monitor);
965 g_object_unref (data.file);
966 g_object_unref (data.output_stream);
970 main (int argc, char *argv[])
972 g_test_init (&argc, &argv, NULL);
974 g_test_bug_base ("https://bugzilla.gnome.org/show_bug.cgi?id=");
976 g_test_add_func ("/monitor/atomic-replace", test_atomic_replace);
977 g_test_add_func ("/monitor/file-changes", test_file_changes);
978 g_test_add_func ("/monitor/dir-monitor", test_dir_monitor);
979 g_test_add_func ("/monitor/dir-not-existent", test_dir_non_existent);
980 g_test_add_func ("/monitor/cross-dir-moves", test_cross_dir_moves);
981 g_test_add_func ("/monitor/file/hard-links", test_file_hard_links);
983 return g_test_run ();