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
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.
34 free_recorded_event (RecordedEvent
*event
)
37 g_free (event
->other_file
);
44 GFileMonitor
*monitor
;
48 GFileOutputStream
*output_stream
;
53 output_event (RecordedEvent
*event
)
56 g_print (">>>> step %d\n", event
->step
);
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
,
66 g_type_class_unref (class);
71 output_events (GList
*list
)
75 g_print (">>>output events\n");
76 for (l
= list
; l
; l
= l
->next
)
77 output_event ((RecordedEvent
*)l
->data
);
81 /* a placeholder for temp file names we don't want to compare */
82 static const gchar DONT_CARE
[] = "";
85 get_environment (GFileMonitor
*monitor
)
87 if (g_str_equal (G_OBJECT_TYPE_NAME (monitor
), "GInotifyFileMonitor"))
89 if (g_str_equal (G_OBJECT_TYPE_NAME (monitor
), "GKqueueFileMonitor"))
95 check_expected_events (RecordedEvent
*expected
,
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
)
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
;
130 e2_next
= l
->next
->data
;
132 if (e2
->event_type
!= G_FILE_MONITOR_EVENT_DELETED
)
134 if (e2_next
->event_type
!= G_FILE_MONITOR_EVENT_CREATED
)
137 if (e1
->step
!= e2_next
->step
)
140 if (e1
->file
!= DONT_CARE
&&
141 (g_strcmp0 (e1
->file
, e2
->file
) != 0 ||
142 e2
->other_file
!= NULL
))
145 if (e1
->other_file
!= DONT_CARE
&&
146 (g_strcmp0 (e1
->other_file
, e2_next
->file
) != 0 ||
147 e2_next
->other_file
!= NULL
))
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
)
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
)
167 ignore_other_file
= TRUE
;
173 if (e1
->file
!= DONT_CARE
&&
174 g_strcmp0 (e1
->file
, e2
->file
) != 0)
177 if (e1
->other_file
!= DONT_CARE
&& !ignore_other_file
&&
178 g_strcmp0 (e1
->other_file
, e2
->other_file
) != 0)
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
);
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
);
212 /* Run above checks under g_assert_* again to provide more useful
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
;
234 g_assert_cmpint (i
, ==, n_expected
);
235 g_assert_cmpint (li
, ==, g_list_length (recorded
));
239 record_event (TestData
*data
,
242 const gchar
*other_file
,
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
);
253 data
->events
= g_list_append (data
->events
, event
);
257 monitor_changed (GFileMonitor
*monitor
,
260 GFileMonitorEvent event_type
,
263 TestData
*data
= user_data
;
264 gchar
*basename
, *other_base
;
266 basename
= g_file_get_basename (file
);
268 other_base
= g_file_get_basename (other_file
);
272 record_event (data
, event_type
, basename
, other_base
, -1);
279 atomic_replace_step (gpointer user_data
)
281 TestData
*data
= user_data
;
282 GError
*error
= NULL
;
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
);
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
);
297 record_event (data
, -1, NULL
, NULL
, 2);
298 g_file_delete (data
->file
, NULL
, NULL
);
301 record_event (data
, -1, NULL
, NULL
, 3);
302 g_main_loop_quit (data
->loop
);
303 return G_SOURCE_REMOVE
;
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
}
325 test_atomic_replace (void)
327 GError
*error
= 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
),
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
);
361 change_step (gpointer user_data
)
363 TestData
*data
= user_data
;
364 GOutputStream
*stream
;
365 GError
*error
= NULL
;
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
);
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
);
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
,
391 G_FILE_QUERY_INFO_NONE
,
394 g_assert_no_error (error
);
397 record_event (data
, -1, NULL
, NULL
, 3);
398 g_file_delete (data
->file
, NULL
, NULL
);
401 record_event (data
, -1, NULL
, NULL
, 4);
402 g_main_loop_quit (data
->loop
);
403 return G_SOURCE_REMOVE
;
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
}
428 test_file_changes (void)
430 GError
*error
= 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
),
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
);
464 dir_step (gpointer user_data
)
466 TestData
*data
= user_data
;
467 GFile
*parent
, *file
, *file2
;
468 GError
*error
= NULL
;
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
);
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
);
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
);
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
);
514 record_event (data
, -1, NULL
, NULL
, 5);
515 g_file_delete (data
->file
, NULL
, NULL
);
518 record_event (data
, -1, NULL
, NULL
, 6);
519 g_main_loop_quit (data
->loop
);
520 return G_SOURCE_REMOVE
;
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
}
543 test_dir_monitor (void)
545 GError
*error
= 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
),
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
);
580 nodir_step (gpointer user_data
)
582 TestData
*data
= user_data
;
584 GError
*error
= NULL
;
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
);
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
);
601 record_event (data
, -1, NULL
, NULL
, 2);
602 g_file_delete (data
->file
, NULL
, &error
);
603 g_assert_no_error (error
);
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
);
613 record_event (data
, -1, NULL
, NULL
, 4);
614 g_main_loop_quit (data
->loop
);
615 return G_SOURCE_REMOVE
;
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
}
638 test_dir_non_existent (void)
641 GError
*error
= 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
),
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
);
675 cross_dir_step (gpointer user_data
)
677 TestData
*data
= user_data
;
679 GError
*error
= NULL
;
681 switch (data
[0].step
)
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
);
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
);
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
);
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
;
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
},
745 test_cross_dir_moves (void)
747 GError
*error
= NULL
;
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]);
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
);
783 g_print ("monitor a:\n");
784 output_events (data
[0].events
);
785 g_print ("monitor b:\n");
786 output_events (data
[1].events
);
789 check_expected_events (cross_dir_a_output
,
790 G_N_ELEMENTS (cross_dir_a_output
),
792 get_environment (data
[0].monitor
));
793 check_expected_events (cross_dir_b_output
,
794 G_N_ELEMENTS (cross_dir_b_output
),
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
);
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
);
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
);
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
);
836 record_event (data
, -1, NULL
, NULL
, 2);
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 */
845 record_event (data
, -1, NULL
, NULL
, 3);
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
,
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 */
864 record_event (data
, -1, NULL
, NULL
, 4);
865 g_file_delete (data
->file
, NULL
, &error
);
866 g_assert_no_error (error
);
869 record_event (data
, -1, NULL
, NULL
, 5);
871 g_file_delete (hard_link_file
, NULL
, &error
);
872 g_assert_no_error (error
);
873 #endif /* HAVE_LINK */
876 record_event (data
, -1, NULL
, NULL
, 6);
877 g_main_loop_quit (data
->loop
);
878 retval
= G_SOURCE_REMOVE
;
882 if (retval
!= G_SOURCE_REMOVE
)
885 g_object_unref (hard_link_file
);
886 g_free (hard_link_name
);
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
},
916 test_file_hard_links (void)
918 GError
*error
= NULL
;
921 g_test_bug ("755721");
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 */
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
,
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
),
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 ();