Branch data Line data Source code
1 : : #include "config.h"
2 : :
3 : : #include <errno.h>
4 : : #include <stdlib.h>
5 : : #include <gio/gio.h>
6 : :
7 : : static gboolean
8 : 6 : skip_win32 (void)
9 : : {
10 : : #ifdef G_OS_WIN32
11 : : g_test_skip ("FIXME, test is broken on win32");
12 : : return TRUE;
13 : : #else
14 : 6 : return FALSE;
15 : : #endif
16 : : }
17 : :
18 : : /* These tests were written for the inotify implementation.
19 : : * Other implementations may require slight adjustments in
20 : : * the tests, e.g. the length of timeouts
21 : : */
22 : :
23 : : typedef struct
24 : : {
25 : : GFile *tmp_dir;
26 : : } Fixture;
27 : :
28 : : static void
29 : 8 : setup (Fixture *fixture,
30 : : gconstpointer user_data)
31 : : {
32 : 8 : gchar *path = NULL;
33 : 8 : GError *local_error = NULL;
34 : :
35 : 8 : path = g_dir_make_tmp ("gio-test-testfilemonitor_XXXXXX", &local_error);
36 : 8 : g_assert_no_error (local_error);
37 : :
38 : 8 : fixture->tmp_dir = g_file_new_for_path (path);
39 : :
40 : 8 : g_test_message ("Using temporary directory: %s", path);
41 : :
42 : 8 : g_free (path);
43 : 8 : }
44 : :
45 : : static void
46 : 8 : teardown (Fixture *fixture,
47 : : gconstpointer user_data)
48 : : {
49 : 8 : GError *local_error = NULL;
50 : :
51 : 8 : g_file_delete (fixture->tmp_dir, NULL, &local_error);
52 : 8 : g_assert_no_error (local_error);
53 : 8 : g_clear_object (&fixture->tmp_dir);
54 : 8 : }
55 : :
56 : : typedef enum {
57 : : NONE = 0,
58 : : INOTIFY = (1 << 1),
59 : : KQUEUE = (1 << 2)
60 : : } G_GNUC_FLAG_ENUM Environment;
61 : :
62 : : typedef struct
63 : : {
64 : : gint event_type;
65 : : gchar *file;
66 : : gchar *other_file;
67 : : gint step;
68 : :
69 : : /* Since different file monitor implementation has different capabilities,
70 : : * we cannot expect all implementations to report all kind of events without
71 : : * any loss. This 'optional' field is a bit mask used to mark events which
72 : : * may be lost under specific platforms.
73 : : */
74 : : Environment optional;
75 : : } RecordedEvent;
76 : :
77 : : static void
78 : 70 : free_recorded_event (RecordedEvent *event)
79 : : {
80 : 70 : g_free (event->file);
81 : 70 : g_free (event->other_file);
82 : 70 : g_free (event);
83 : 70 : }
84 : :
85 : : typedef struct
86 : : {
87 : : GFile *file;
88 : : GFileMonitor *monitor;
89 : : GMainLoop *loop;
90 : : gint step;
91 : : GList *events;
92 : : GFileOutputStream *output_stream;
93 : : } TestData;
94 : :
95 : : static void
96 : 0 : output_event (const RecordedEvent *event)
97 : : {
98 : 0 : if (event->step >= 0)
99 : 0 : g_test_message (">>>> step %d", event->step);
100 : : else
101 : : {
102 : : GTypeClass *class;
103 : :
104 : 0 : class = g_type_class_ref (g_type_from_name ("GFileMonitorEvent"));
105 : 0 : g_test_message ("%s file=%s other_file=%s\n",
106 : 0 : g_enum_get_value (G_ENUM_CLASS (class), event->event_type)->value_nick,
107 : 0 : event->file,
108 : 0 : event->other_file);
109 : 0 : g_type_class_unref (class);
110 : : }
111 : 0 : }
112 : :
113 : : /* a placeholder for temp file names we don't want to compare */
114 : : static const gchar DONT_CARE[] = "";
115 : :
116 : : static Environment
117 : 7 : get_environment (GFileMonitor *monitor)
118 : : {
119 : : #if defined(FILE_MONITOR_BACKEND_INOTIFY)
120 : 7 : return INOTIFY;
121 : : #elif defined(FILE_MONITOR_BACKEND_KQUEUE)
122 : : return KQUEUE;
123 : : #elif defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
124 : : return INOTIFY | KQUEUE;
125 : : #else
126 : : return NONE;
127 : : #endif
128 : : }
129 : :
130 : : static void
131 : 7 : check_expected_events (RecordedEvent *expected,
132 : : gsize n_expected,
133 : : GList *recorded,
134 : : Environment env)
135 : : {
136 : : gsize i;
137 : : gint li;
138 : : GList *l;
139 : :
140 : 79 : for (i = 0, li = 0, l = recorded; i < n_expected && l != NULL;)
141 : : {
142 : 72 : RecordedEvent *e1 = &expected[i];
143 : 72 : RecordedEvent *e2 = l->data;
144 : 72 : gboolean mismatch = TRUE;
145 : 72 : gboolean l_extra_step = FALSE;
146 : :
147 : : do
148 : : {
149 : 72 : gboolean ignore_other_file = FALSE;
150 : :
151 : 72 : if (e1->step != e2->step)
152 : 2 : break;
153 : :
154 : : /* Kqueue isn't good at detecting file renaming, so
155 : : * G_FILE_MONITOR_WATCH_MOVES is mostly useless there. */
156 : 70 : if (e1->event_type != e2->event_type && env & KQUEUE)
157 : : {
158 : : /* It is possible for kqueue file monitor to emit 'RENAMED' event,
159 : : * but most of the time it is reported as a 'DELETED' event and
160 : : * a 'CREATED' event. */
161 : 0 : if (e1->event_type == G_FILE_MONITOR_EVENT_RENAMED)
162 : : {
163 : : RecordedEvent *e2_next;
164 : :
165 : 0 : if (l->next == NULL)
166 : 0 : break;
167 : 0 : e2_next = l->next->data;
168 : :
169 : 0 : if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
170 : 0 : break;
171 : 0 : if (e2_next->event_type != G_FILE_MONITOR_EVENT_CREATED)
172 : 0 : break;
173 : :
174 : 0 : if (e1->step != e2_next->step)
175 : 0 : break;
176 : :
177 : 0 : if (e1->file != DONT_CARE &&
178 : 0 : (g_strcmp0 (e1->file, e2->file) != 0 ||
179 : 0 : e2->other_file != NULL))
180 : : break;
181 : :
182 : 0 : if (e1->other_file != DONT_CARE &&
183 : 0 : (g_strcmp0 (e1->other_file, e2_next->file) != 0 ||
184 : 0 : e2_next->other_file != NULL))
185 : : break;
186 : :
187 : 0 : l_extra_step = TRUE;
188 : 0 : mismatch = FALSE;
189 : 0 : break;
190 : : }
191 : : /* Kqueue won't report 'MOVED_IN' and 'MOVED_OUT' events. We set
192 : : * 'ignore_other_file' here to let the following code know that
193 : : * 'other_file' may not match. */
194 : 0 : else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_IN)
195 : : {
196 : 0 : if (e2->event_type != G_FILE_MONITOR_EVENT_CREATED)
197 : 0 : break;
198 : 0 : ignore_other_file = TRUE;
199 : : }
200 : 0 : else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_OUT)
201 : : {
202 : 0 : if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
203 : 0 : break;
204 : 0 : ignore_other_file = TRUE;
205 : : }
206 : : else
207 : 0 : break;
208 : : }
209 : :
210 : 138 : if (e1->file != DONT_CARE &&
211 : 68 : g_strcmp0 (e1->file, e2->file) != 0)
212 : 0 : break;
213 : :
214 : 140 : if (e1->other_file != DONT_CARE && !ignore_other_file &&
215 : 70 : g_strcmp0 (e1->other_file, e2->other_file) != 0)
216 : 0 : break;
217 : :
218 : 70 : mismatch = FALSE;
219 : : }
220 : : while (0);
221 : :
222 : 72 : if (mismatch)
223 : : {
224 : : /* Sometimes the emission of 'CHANGES_DONE_HINT' may be late because
225 : : * it depends on the ability of file monitor implementation to report
226 : : * 'CHANGES_DONE_HINT' itself. If the file monitor implementation
227 : : * doesn't report 'CHANGES_DONE_HINT' itself, it may be emitted by
228 : : * GLocalFileMonitor after a few seconds, which causes the event to
229 : : * mix with results from different steps. Since 'CHANGES_DONE_HINT'
230 : : * is just a hint, we don't require it to be reliable and we simply
231 : : * ignore unexpected 'CHANGES_DONE_HINT' events here. */
232 : 2 : if (e1->event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
233 : 2 : e2->event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
234 : : {
235 : 0 : g_test_message ("Event CHANGES_DONE_HINT ignored at "
236 : : "expected index %" G_GSIZE_FORMAT ", recorded index %d", i, li);
237 : 0 : li++, l = l->next;
238 : 0 : continue;
239 : : }
240 : : /* The ordering of 'CHANGES_DONE_HINT' can not be guaranteed
241 : : * so treat it as optional. */
242 : 2 : else if (e1->event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
243 : 0 : e2->event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
244 : : {
245 : 0 : g_test_message ("Skipping expected 'CHANGES_DONE_HINT' "
246 : : "at index %" G_GSIZE_FORMAT,
247 : : i);
248 : 0 : i++;
249 : 0 : continue;
250 : : }
251 : : /* If an event is marked as optional in the current environment and
252 : : * the event doesn't match, it means the expected event has lost. */
253 : 2 : else if (env & e1->optional)
254 : : {
255 : 2 : g_test_message ("Event %d at expected index %" G_GSIZE_FORMAT " skipped because "
256 : : "it is marked as optional", e1->event_type, i);
257 : 2 : i++;
258 : 2 : continue;
259 : : }
260 : : /* Run above checks under g_assert_* again to provide more useful
261 : : * error messages. Print the expected and actual events first. */
262 : : else
263 : : {
264 : : GList *ll;
265 : : gsize j;
266 : :
267 : 0 : g_test_message ("Recorded events:");
268 : 0 : for (ll = recorded; ll != NULL; ll = ll->next)
269 : 0 : output_event ((RecordedEvent *) ll->data);
270 : :
271 : 0 : g_test_message ("Expected events:");
272 : 0 : for (j = 0; j < n_expected; j++)
273 : 0 : output_event (&expected[j]);
274 : :
275 : 0 : g_assert_cmpint (e1->step, ==, e2->step);
276 : 0 : g_assert_cmpint (e1->event_type, ==, e2->event_type);
277 : :
278 : 0 : if (e1->file != DONT_CARE)
279 : 0 : g_assert_cmpstr (e1->file, ==, e2->file);
280 : :
281 : 0 : if (e1->other_file != DONT_CARE)
282 : 0 : g_assert_cmpstr (e1->other_file, ==, e2->other_file);
283 : :
284 : : g_assert_not_reached ();
285 : : }
286 : : }
287 : :
288 : 70 : i++, li++, l = l->next;
289 : 70 : if (l_extra_step)
290 : 0 : li++, l = l->next;
291 : : }
292 : :
293 : 7 : g_assert_cmpint (i, ==, n_expected);
294 : 7 : g_assert_cmpint (li, ==, g_list_length (recorded));
295 : 7 : }
296 : :
297 : : static void
298 : 70 : record_event (TestData *data,
299 : : gint event_type,
300 : : const gchar *file,
301 : : const gchar *other_file,
302 : : gint step)
303 : : {
304 : : RecordedEvent *event;
305 : :
306 : 70 : event = g_new0 (RecordedEvent, 1);
307 : 70 : event->event_type = event_type;
308 : 70 : event->file = g_strdup (file);
309 : 70 : event->other_file = g_strdup (other_file);
310 : 70 : event->step = step;
311 : :
312 : 70 : data->events = g_list_append (data->events, event);
313 : 70 : }
314 : :
315 : : static void
316 : 35 : monitor_changed (GFileMonitor *monitor,
317 : : GFile *file,
318 : : GFile *other_file,
319 : : GFileMonitorEvent event_type,
320 : : gpointer user_data)
321 : : {
322 : 35 : TestData *data = user_data;
323 : : gchar *basename, *other_base;
324 : :
325 : 35 : basename = g_file_get_basename (file);
326 : 35 : if (other_file)
327 : 4 : other_base = g_file_get_basename (other_file);
328 : : else
329 : 31 : other_base = NULL;
330 : :
331 : 35 : record_event (data, event_type, basename, other_base, -1);
332 : :
333 : 35 : g_free (basename);
334 : 35 : g_free (other_base);
335 : 35 : }
336 : :
337 : : static gboolean
338 : 4 : atomic_replace_step (gpointer user_data)
339 : : {
340 : 4 : TestData *data = user_data;
341 : 4 : GError *error = NULL;
342 : :
343 : 4 : switch (data->step)
344 : : {
345 : 1 : case 0:
346 : 1 : record_event (data, -1, NULL, NULL, 0);
347 : 1 : g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
348 : 1 : g_assert_no_error (error);
349 : 1 : break;
350 : 1 : case 1:
351 : 1 : record_event (data, -1, NULL, NULL, 1);
352 : 1 : g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
353 : 1 : g_assert_no_error (error);
354 : 1 : break;
355 : 1 : case 2:
356 : 1 : record_event (data, -1, NULL, NULL, 2);
357 : 1 : g_file_delete (data->file, NULL, NULL);
358 : 1 : break;
359 : 1 : case 3:
360 : 1 : record_event (data, -1, NULL, NULL, 3);
361 : 1 : g_main_loop_quit (data->loop);
362 : 1 : return G_SOURCE_REMOVE;
363 : : }
364 : :
365 : 3 : data->step++;
366 : :
367 : 3 : return G_SOURCE_CONTINUE;
368 : : }
369 : :
370 : : /* this is the output we expect from the above steps */
371 : : static RecordedEvent atomic_replace_output[] = {
372 : : { -1, NULL, NULL, 0, NONE },
373 : : { G_FILE_MONITOR_EVENT_CREATED, "atomic_replace_file", NULL, -1, NONE },
374 : : { G_FILE_MONITOR_EVENT_CHANGED, "atomic_replace_file", NULL, -1, KQUEUE },
375 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "atomic_replace_file", NULL, -1, KQUEUE },
376 : : { -1, NULL, NULL, 1, NONE },
377 : : { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE, "atomic_replace_file", -1, NONE },
378 : : { -1, NULL, NULL, 2, NONE },
379 : : { G_FILE_MONITOR_EVENT_DELETED, "atomic_replace_file", NULL, -1, NONE },
380 : : { -1, NULL, NULL, 3, NONE }
381 : : };
382 : :
383 : : static void
384 : 1 : test_atomic_replace (Fixture *fixture,
385 : : gconstpointer user_data)
386 : : {
387 : 1 : GError *error = NULL;
388 : : TestData data;
389 : :
390 : 1 : if (skip_win32 ())
391 : 0 : return;
392 : :
393 : 1 : data.step = 0;
394 : 1 : data.events = NULL;
395 : :
396 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "atomic_replace_file");
397 : 1 : g_file_delete (data.file, NULL, NULL);
398 : :
399 : 1 : data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
400 : 1 : g_assert_no_error (error);
401 : :
402 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
403 : :
404 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
405 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
406 : :
407 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
408 : :
409 : 1 : g_timeout_add (500, atomic_replace_step, &data);
410 : :
411 : 1 : g_main_loop_run (data.loop);
412 : :
413 : 1 : check_expected_events (atomic_replace_output,
414 : : G_N_ELEMENTS (atomic_replace_output),
415 : : data.events,
416 : : get_environment (data.monitor));
417 : :
418 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
419 : 1 : g_main_loop_unref (data.loop);
420 : 1 : g_object_unref (data.monitor);
421 : 1 : g_object_unref (data.file);
422 : : }
423 : :
424 : : static gboolean
425 : 5 : change_step (gpointer user_data)
426 : : {
427 : 5 : TestData *data = user_data;
428 : : GOutputStream *stream;
429 : 5 : GError *error = NULL;
430 : 5 : guint32 mode = 0660;
431 : :
432 : 5 : switch (data->step)
433 : : {
434 : 1 : case 0:
435 : 1 : record_event (data, -1, NULL, NULL, 0);
436 : 1 : g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
437 : 1 : g_assert_no_error (error);
438 : 1 : break;
439 : 1 : case 1:
440 : 1 : record_event (data, -1, NULL, NULL, 1);
441 : 1 : stream = (GOutputStream *)g_file_append_to (data->file, G_FILE_CREATE_NONE, NULL, &error);
442 : 1 : g_assert_no_error (error);
443 : 1 : g_output_stream_write_all (stream, " step 1", 7, NULL, NULL, &error);
444 : 1 : g_assert_no_error (error);
445 : 1 : g_output_stream_close (stream, NULL, &error);
446 : 1 : g_assert_no_error (error);
447 : 1 : g_object_unref (stream);
448 : 1 : break;
449 : 1 : case 2:
450 : 1 : record_event (data, -1, NULL, NULL, 2);
451 : 1 : g_file_set_attribute (data->file,
452 : : G_FILE_ATTRIBUTE_UNIX_MODE,
453 : : G_FILE_ATTRIBUTE_TYPE_UINT32,
454 : : &mode,
455 : : G_FILE_QUERY_INFO_NONE,
456 : : NULL,
457 : : &error);
458 : 1 : g_assert_no_error (error);
459 : 1 : break;
460 : 1 : case 3:
461 : 1 : record_event (data, -1, NULL, NULL, 3);
462 : 1 : g_file_delete (data->file, NULL, NULL);
463 : 1 : break;
464 : 1 : case 4:
465 : 1 : record_event (data, -1, NULL, NULL, 4);
466 : 1 : g_main_loop_quit (data->loop);
467 : 1 : return G_SOURCE_REMOVE;
468 : : }
469 : :
470 : 4 : data->step++;
471 : :
472 : 4 : return G_SOURCE_CONTINUE;
473 : : }
474 : :
475 : : /* this is the output we expect from the above steps */
476 : : static RecordedEvent change_output[] = {
477 : : { -1, NULL, NULL, 0, NONE },
478 : : { G_FILE_MONITOR_EVENT_CREATED, "change_file", NULL, -1, NONE },
479 : : { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, KQUEUE },
480 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, KQUEUE },
481 : : { -1, NULL, NULL, 1, NONE },
482 : : { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, NONE },
483 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, NONE },
484 : : { -1, NULL, NULL, 2, NONE },
485 : : { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, "change_file", NULL, -1, NONE },
486 : : { -1, NULL, NULL, 3, NONE },
487 : : { G_FILE_MONITOR_EVENT_DELETED, "change_file", NULL, -1, NONE },
488 : : { -1, NULL, NULL, 4, NONE }
489 : : };
490 : :
491 : : static void
492 : 1 : test_file_changes (Fixture *fixture,
493 : : gconstpointer user_data)
494 : : {
495 : 1 : GError *error = NULL;
496 : : TestData data;
497 : :
498 : 1 : if (skip_win32 ())
499 : 0 : return;
500 : :
501 : 1 : data.step = 0;
502 : 1 : data.events = NULL;
503 : :
504 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "change_file");
505 : 1 : g_file_delete (data.file, NULL, NULL);
506 : :
507 : 1 : data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
508 : 1 : g_assert_no_error (error);
509 : :
510 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
511 : :
512 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
513 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
514 : :
515 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
516 : :
517 : 1 : g_timeout_add (500, change_step, &data);
518 : :
519 : 1 : g_main_loop_run (data.loop);
520 : :
521 : 1 : check_expected_events (change_output,
522 : : G_N_ELEMENTS (change_output),
523 : : data.events,
524 : : get_environment (data.monitor));
525 : :
526 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
527 : 1 : g_main_loop_unref (data.loop);
528 : 1 : g_object_unref (data.monitor);
529 : 1 : g_object_unref (data.file);
530 : : }
531 : :
532 : : static gboolean
533 : 7 : dir_step (gpointer user_data)
534 : : {
535 : 7 : TestData *data = user_data;
536 : : GFile *parent, *file, *file2;
537 : 7 : GError *error = NULL;
538 : :
539 : 7 : switch (data->step)
540 : : {
541 : 1 : case 1:
542 : 1 : record_event (data, -1, NULL, NULL, 1);
543 : 1 : parent = g_file_get_parent (data->file);
544 : 1 : file = g_file_get_child (parent, "dir_test_file");
545 : 1 : g_file_replace_contents (file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
546 : 1 : g_assert_no_error (error);
547 : 1 : g_object_unref (file);
548 : 1 : g_object_unref (parent);
549 : 1 : break;
550 : 1 : case 2:
551 : 1 : record_event (data, -1, NULL, NULL, 2);
552 : 1 : parent = g_file_get_parent (data->file);
553 : 1 : file = g_file_get_child (parent, "dir_test_file");
554 : 1 : file2 = g_file_get_child (data->file, "dir_test_file");
555 : 1 : g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
556 : 1 : g_assert_no_error (error);
557 : 1 : g_object_unref (file);
558 : 1 : g_object_unref (file2);
559 : 1 : g_object_unref (parent);
560 : 1 : break;
561 : 1 : case 3:
562 : 1 : record_event (data, -1, NULL, NULL, 3);
563 : 1 : file = g_file_get_child (data->file, "dir_test_file");
564 : 1 : file2 = g_file_get_child (data->file, "dir_test_file2");
565 : 1 : g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
566 : 1 : g_assert_no_error (error);
567 : 1 : g_object_unref (file);
568 : 1 : g_object_unref (file2);
569 : 1 : break;
570 : 1 : case 4:
571 : 1 : record_event (data, -1, NULL, NULL, 4);
572 : 1 : parent = g_file_get_parent (data->file);
573 : 1 : file = g_file_get_child (data->file, "dir_test_file2");
574 : 1 : file2 = g_file_get_child (parent, "dir_test_file2");
575 : 1 : g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
576 : 1 : g_assert_no_error (error);
577 : 1 : g_file_delete (file2, NULL, NULL);
578 : 1 : g_object_unref (file);
579 : 1 : g_object_unref (file2);
580 : 1 : g_object_unref (parent);
581 : 1 : break;
582 : 1 : case 5:
583 : 1 : record_event (data, -1, NULL, NULL, 5);
584 : 1 : g_file_delete (data->file, NULL, NULL);
585 : 1 : break;
586 : 1 : case 6:
587 : 1 : record_event (data, -1, NULL, NULL, 6);
588 : 1 : g_main_loop_quit (data->loop);
589 : 1 : return G_SOURCE_REMOVE;
590 : : }
591 : :
592 : 6 : data->step++;
593 : :
594 : 6 : return G_SOURCE_CONTINUE;
595 : : }
596 : :
597 : : /* this is the output we expect from the above steps */
598 : : static RecordedEvent dir_output[] = {
599 : : { -1, NULL, NULL, 1, NONE },
600 : : { -1, NULL, NULL, 2, NONE },
601 : : { G_FILE_MONITOR_EVENT_MOVED_IN, "dir_test_file", NULL, -1, NONE },
602 : : { -1, NULL, NULL, 3, NONE },
603 : : { G_FILE_MONITOR_EVENT_RENAMED, "dir_test_file", "dir_test_file2", -1, NONE },
604 : : { -1, NULL, NULL, 4, NONE },
605 : : { G_FILE_MONITOR_EVENT_MOVED_OUT, "dir_test_file2", NULL, -1, NONE },
606 : : { -1, NULL, NULL, 5, NONE },
607 : : { G_FILE_MONITOR_EVENT_DELETED, "dir_monitor_test", NULL, -1, NONE },
608 : : { -1, NULL, NULL, 6, NONE }
609 : : };
610 : :
611 : : static void
612 : 1 : test_dir_monitor (Fixture *fixture,
613 : : gconstpointer user_data)
614 : : {
615 : 1 : GError *error = NULL;
616 : : TestData data;
617 : :
618 : 1 : if (skip_win32 ())
619 : 0 : return;
620 : :
621 : 1 : data.step = 0;
622 : 1 : data.events = NULL;
623 : :
624 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "dir_monitor_test");
625 : 1 : g_file_delete (data.file, NULL, NULL);
626 : 1 : g_file_make_directory (data.file, NULL, &error);
627 : :
628 : 1 : data.monitor = g_file_monitor_directory (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
629 : 1 : g_assert_no_error (error);
630 : :
631 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
632 : :
633 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
634 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
635 : :
636 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
637 : :
638 : 1 : g_timeout_add (500, dir_step, &data);
639 : :
640 : 1 : g_main_loop_run (data.loop);
641 : :
642 : 1 : check_expected_events (dir_output,
643 : : G_N_ELEMENTS (dir_output),
644 : : data.events,
645 : : get_environment (data.monitor));
646 : :
647 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
648 : 1 : g_main_loop_unref (data.loop);
649 : 1 : g_object_unref (data.monitor);
650 : 1 : g_object_unref (data.file);
651 : : }
652 : :
653 : : static gboolean
654 : 5 : nodir_step (gpointer user_data)
655 : : {
656 : 5 : TestData *data = user_data;
657 : : GFile *parent;
658 : 5 : GError *error = NULL;
659 : :
660 : 5 : switch (data->step)
661 : : {
662 : 1 : case 0:
663 : 1 : record_event (data, -1, NULL, NULL, 0);
664 : 1 : parent = g_file_get_parent (data->file);
665 : 1 : g_file_make_directory (parent, NULL, &error);
666 : 1 : g_assert_no_error (error);
667 : 1 : g_object_unref (parent);
668 : 1 : break;
669 : 1 : case 1:
670 : 1 : record_event (data, -1, NULL, NULL, 1);
671 : 1 : g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
672 : 1 : g_assert_no_error (error);
673 : 1 : break;
674 : 1 : case 2:
675 : 1 : record_event (data, -1, NULL, NULL, 2);
676 : 1 : g_file_delete (data->file, NULL, &error);
677 : 1 : g_assert_no_error (error);
678 : 1 : break;
679 : 1 : case 3:
680 : 1 : record_event (data, -1, NULL, NULL, 3);
681 : 1 : parent = g_file_get_parent (data->file);
682 : 1 : g_file_delete (parent, NULL, &error);
683 : 1 : g_assert_no_error (error);
684 : 1 : g_object_unref (parent);
685 : 1 : break;
686 : 1 : case 4:
687 : 1 : record_event (data, -1, NULL, NULL, 4);
688 : 1 : g_main_loop_quit (data->loop);
689 : 1 : return G_SOURCE_REMOVE;
690 : : }
691 : :
692 : 4 : data->step++;
693 : :
694 : 4 : return G_SOURCE_CONTINUE;
695 : : }
696 : :
697 : : static RecordedEvent nodir_output[] = {
698 : : { -1, NULL, NULL, 0, NONE },
699 : : { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, KQUEUE },
700 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
701 : : { -1, NULL, NULL, 1, NONE },
702 : : { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, NONE },
703 : : { G_FILE_MONITOR_EVENT_CHANGED, "nosuchfile", NULL, -1, KQUEUE },
704 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
705 : : { -1, NULL, NULL, 2, NONE },
706 : : { G_FILE_MONITOR_EVENT_DELETED, "nosuchfile", NULL, -1, NONE },
707 : : { -1, NULL, NULL, 3, NONE },
708 : : { -1, NULL, NULL, 4, NONE }
709 : : };
710 : :
711 : : static void
712 : 1 : test_dir_non_existent (Fixture *fixture,
713 : : gconstpointer user_data)
714 : : {
715 : : TestData data;
716 : 1 : GError *error = NULL;
717 : :
718 : 1 : if (skip_win32 ())
719 : 0 : return;
720 : :
721 : 1 : data.step = 0;
722 : 1 : data.events = NULL;
723 : :
724 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "nosuchdir/nosuchfile");
725 : 1 : data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
726 : 1 : g_assert_no_error (error);
727 : :
728 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
729 : :
730 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
731 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
732 : :
733 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
734 : :
735 : : /* we need a long timeout here, since the inotify implementation only scans
736 : : * for missing files every 4 seconds.
737 : : */
738 : 1 : g_timeout_add (5000, nodir_step, &data);
739 : :
740 : 1 : g_main_loop_run (data.loop);
741 : :
742 : 1 : check_expected_events (nodir_output,
743 : : G_N_ELEMENTS (nodir_output),
744 : : data.events,
745 : : get_environment (data.monitor));
746 : :
747 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
748 : 1 : g_main_loop_unref (data.loop);
749 : 1 : g_object_unref (data.monitor);
750 : 1 : g_object_unref (data.file);
751 : : }
752 : :
753 : : static gboolean
754 : 4 : cross_dir_step (gpointer user_data)
755 : : {
756 : 4 : TestData *data = user_data;
757 : : GFile *file, *file2;
758 : 4 : GError *error = NULL;
759 : :
760 : 4 : switch (data[0].step)
761 : : {
762 : 1 : case 0:
763 : 1 : record_event (&data[0], -1, NULL, NULL, 0);
764 : 1 : record_event (&data[1], -1, NULL, NULL, 0);
765 : 1 : file = g_file_get_child (data[1].file, "a");
766 : 1 : g_file_replace_contents (file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
767 : 1 : g_assert_no_error (error);
768 : 1 : g_object_unref (file);
769 : 1 : break;
770 : 1 : case 1:
771 : 1 : record_event (&data[0], -1, NULL, NULL, 1);
772 : 1 : record_event (&data[1], -1, NULL, NULL, 1);
773 : 1 : file = g_file_get_child (data[1].file, "a");
774 : 1 : file2 = g_file_get_child (data[0].file, "a");
775 : 1 : g_file_move (file, file2, 0, NULL, NULL, NULL, &error);
776 : 1 : g_assert_no_error (error);
777 : 1 : g_object_unref (file);
778 : 1 : g_object_unref (file2);
779 : 1 : break;
780 : 1 : case 2:
781 : 1 : record_event (&data[0], -1, NULL, NULL, 2);
782 : 1 : record_event (&data[1], -1, NULL, NULL, 2);
783 : 1 : file2 = g_file_get_child (data[0].file, "a");
784 : 1 : g_file_delete (file2, NULL, NULL);
785 : 1 : g_file_delete (data[0].file, NULL, NULL);
786 : 1 : g_file_delete (data[1].file, NULL, NULL);
787 : 1 : g_object_unref (file2);
788 : 1 : break;
789 : 1 : case 3:
790 : 1 : record_event (&data[0], -1, NULL, NULL, 3);
791 : 1 : record_event (&data[1], -1, NULL, NULL, 3);
792 : 1 : g_main_loop_quit (data->loop);
793 : 1 : return G_SOURCE_REMOVE;
794 : : }
795 : :
796 : 3 : data->step++;
797 : :
798 : 3 : return G_SOURCE_CONTINUE;
799 : : }
800 : :
801 : : static RecordedEvent cross_dir_a_output[] = {
802 : : { -1, NULL, NULL, 0, NONE },
803 : : { -1, NULL, NULL, 1, NONE },
804 : : { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
805 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
806 : : { -1, NULL, NULL, 2, NONE },
807 : : { G_FILE_MONITOR_EVENT_DELETED, "a", NULL, -1, NONE },
808 : : { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_a", NULL, -1, NONE },
809 : : { -1, NULL, NULL, 3, NONE },
810 : : };
811 : :
812 : : static RecordedEvent cross_dir_b_output[] = {
813 : : { -1, NULL, NULL, 0, NONE },
814 : : { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
815 : : { G_FILE_MONITOR_EVENT_CHANGED, "a", NULL, -1, KQUEUE },
816 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
817 : : { -1, NULL, NULL, 1, NONE },
818 : : { G_FILE_MONITOR_EVENT_MOVED_OUT, "a", "a", -1, NONE },
819 : : { -1, NULL, NULL, 2, NONE },
820 : : { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_b", NULL, -1, NONE },
821 : : { -1, NULL, NULL, 3, NONE },
822 : : };
823 : : static void
824 : 1 : test_cross_dir_moves (Fixture *fixture,
825 : : gconstpointer user_data)
826 : : {
827 : 1 : GError *error = NULL;
828 : : TestData data[2];
829 : :
830 : 1 : if (skip_win32 ())
831 : 0 : return;
832 : :
833 : 1 : data[0].step = 0;
834 : 1 : data[0].events = NULL;
835 : :
836 : 1 : data[0].file = g_file_get_child (fixture->tmp_dir, "cross_dir_a");
837 : 1 : g_file_delete (data[0].file, NULL, NULL);
838 : 1 : g_file_make_directory (data[0].file, NULL, &error);
839 : :
840 : 1 : data[0].monitor = g_file_monitor_directory (data[0].file, 0, NULL, &error);
841 : 1 : g_assert_no_error (error);
842 : :
843 : 1 : g_test_message ("Using GFileMonitor 0 %s", G_OBJECT_TYPE_NAME (data[0].monitor));
844 : :
845 : 1 : g_file_monitor_set_rate_limit (data[0].monitor, 200);
846 : 1 : g_signal_connect (data[0].monitor, "changed", G_CALLBACK (monitor_changed), &data[0]);
847 : :
848 : 1 : data[1].step = 0;
849 : 1 : data[1].events = NULL;
850 : :
851 : 1 : data[1].file = g_file_get_child (fixture->tmp_dir, "cross_dir_b");
852 : 1 : g_file_delete (data[1].file, NULL, NULL);
853 : 1 : g_file_make_directory (data[1].file, NULL, &error);
854 : :
855 : 1 : data[1].monitor = g_file_monitor_directory (data[1].file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
856 : 1 : g_assert_no_error (error);
857 : :
858 : 1 : g_test_message ("Using GFileMonitor 1 %s", G_OBJECT_TYPE_NAME (data[1].monitor));
859 : :
860 : 1 : g_file_monitor_set_rate_limit (data[1].monitor, 200);
861 : 1 : g_signal_connect (data[1].monitor, "changed", G_CALLBACK (monitor_changed), &data[1]);
862 : :
863 : 1 : data[0].loop = g_main_loop_new (NULL, TRUE);
864 : :
865 : 1 : g_timeout_add (500, cross_dir_step, data);
866 : :
867 : 1 : g_main_loop_run (data[0].loop);
868 : :
869 : 1 : check_expected_events (cross_dir_a_output,
870 : : G_N_ELEMENTS (cross_dir_a_output),
871 : : data[0].events,
872 : : get_environment (data[0].monitor));
873 : 1 : check_expected_events (cross_dir_b_output,
874 : : G_N_ELEMENTS (cross_dir_b_output),
875 : : data[1].events,
876 : : get_environment (data[1].monitor));
877 : :
878 : 1 : g_list_free_full (data[0].events, (GDestroyNotify)free_recorded_event);
879 : 1 : g_main_loop_unref (data[0].loop);
880 : 1 : g_object_unref (data[0].monitor);
881 : 1 : g_object_unref (data[0].file);
882 : :
883 : 1 : g_list_free_full (data[1].events, (GDestroyNotify)free_recorded_event);
884 : 1 : g_object_unref (data[1].monitor);
885 : 1 : g_object_unref (data[1].file);
886 : : }
887 : :
888 : : static gboolean
889 : 7 : file_hard_links_step (gpointer user_data)
890 : : {
891 : 7 : gboolean retval = G_SOURCE_CONTINUE;
892 : 7 : TestData *data = user_data;
893 : 7 : GError *error = NULL;
894 : :
895 : 7 : gchar *filename = g_file_get_path (data->file);
896 : 7 : gchar *hard_link_name = g_strdup_printf ("%s2", filename);
897 : 7 : GFile *hard_link_file = g_file_new_for_path (hard_link_name);
898 : :
899 : 7 : switch (data->step)
900 : : {
901 : 1 : case 0:
902 : 1 : record_event (data, -1, NULL, NULL, 0);
903 : 1 : g_output_stream_write_all (G_OUTPUT_STREAM (data->output_stream),
904 : : "hello, step 0", 13, NULL, NULL, &error);
905 : 1 : g_assert_no_error (error);
906 : 1 : g_output_stream_close (G_OUTPUT_STREAM (data->output_stream), NULL, &error);
907 : 1 : g_assert_no_error (error);
908 : 1 : break;
909 : 1 : case 1:
910 : 1 : record_event (data, -1, NULL, NULL, 1);
911 : 1 : g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE,
912 : : G_FILE_CREATE_NONE, NULL, NULL, &error);
913 : 1 : g_assert_no_error (error);
914 : 1 : break;
915 : 1 : case 2:
916 : 1 : record_event (data, -1, NULL, NULL, 2);
917 : : #ifdef HAVE_LINK
918 : 1 : if (link (filename, hard_link_name) < 0)
919 : : {
920 : 0 : g_error ("link(%s, %s) failed: %s", filename, hard_link_name, g_strerror (errno));
921 : : }
922 : : #endif /* HAVE_LINK */
923 : 1 : break;
924 : 1 : case 3:
925 : 1 : record_event (data, -1, NULL, NULL, 3);
926 : : #ifdef HAVE_LINK
927 : : {
928 : 1 : GOutputStream *hard_link_stream = NULL;
929 : :
930 : : /* Deliberately don’t do an atomic swap on the hard-linked file. */
931 : 1 : hard_link_stream = G_OUTPUT_STREAM (g_file_append_to (hard_link_file,
932 : : G_FILE_CREATE_NONE,
933 : : NULL, &error));
934 : 1 : g_assert_no_error (error);
935 : 1 : g_output_stream_write_all (hard_link_stream, " step 3", 7, NULL, NULL, &error);
936 : 1 : g_assert_no_error (error);
937 : 1 : g_output_stream_close (hard_link_stream, NULL, &error);
938 : 1 : g_assert_no_error (error);
939 : 1 : g_object_unref (hard_link_stream);
940 : : }
941 : : #endif /* HAVE_LINK */
942 : 1 : break;
943 : 1 : case 4:
944 : 1 : record_event (data, -1, NULL, NULL, 4);
945 : 1 : g_file_delete (data->file, NULL, &error);
946 : 1 : g_assert_no_error (error);
947 : 1 : break;
948 : 1 : case 5:
949 : 1 : record_event (data, -1, NULL, NULL, 5);
950 : : #ifdef HAVE_LINK
951 : 1 : g_file_delete (hard_link_file, NULL, &error);
952 : 1 : g_assert_no_error (error);
953 : : #endif /* HAVE_LINK */
954 : 1 : break;
955 : 1 : case 6:
956 : 1 : record_event (data, -1, NULL, NULL, 6);
957 : 1 : g_main_loop_quit (data->loop);
958 : 1 : retval = G_SOURCE_REMOVE;
959 : 1 : break;
960 : : }
961 : :
962 : 7 : if (retval != G_SOURCE_REMOVE)
963 : 6 : data->step++;
964 : :
965 : 7 : g_object_unref (hard_link_file);
966 : 7 : g_free (hard_link_name);
967 : 7 : g_free (filename);
968 : :
969 : 7 : return retval;
970 : : }
971 : :
972 : : static RecordedEvent file_hard_links_output[] = {
973 : : { -1, NULL, NULL, 0, NONE },
974 : : { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, NONE },
975 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "testfilemonitor.db", NULL, -1, NONE },
976 : : { -1, NULL, NULL, 1, NONE },
977 : : { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE /* .goutputstream-XXXXXX */, "testfilemonitor.db", -1, NONE },
978 : : { -1, NULL, NULL, 2, NONE },
979 : : { -1, NULL, NULL, 3, NONE },
980 : : /* Kqueue is based on file descriptors. You can get events from all hard
981 : : * links by just monitoring one open file descriptor, and it is not possible
982 : : * to know whether it is done on the file name we use to open the file. Since
983 : : * the hard link count of 'testfilemonitor.db' is 2, it is expected to see
984 : : * two 'DELETED' events reported here. You have to call 'unlink' twice on
985 : : * different file names to remove 'testfilemonitor.db' from the file system,
986 : : * and each 'unlink' call generates a 'DELETED' event. */
987 : : { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, INOTIFY },
988 : : { -1, NULL, NULL, 4, NONE },
989 : : { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, NONE },
990 : : { -1, NULL, NULL, 5, NONE },
991 : : { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, INOTIFY },
992 : : { -1, NULL, NULL, 6, NONE },
993 : : };
994 : :
995 : : static void
996 : 1 : test_file_hard_links (Fixture *fixture,
997 : : gconstpointer user_data)
998 : : {
999 : 1 : GError *error = NULL;
1000 : : TestData data;
1001 : :
1002 : 1 : g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755721");
1003 : :
1004 : 1 : if (skip_win32 ())
1005 : 0 : return;
1006 : :
1007 : : #ifdef HAVE_LINK
1008 : 1 : g_test_message ("Running with hard link tests");
1009 : : #else /* if !HAVE_LINK */
1010 : : g_test_message ("Running without hard link tests");
1011 : : #endif /* !HAVE_LINK */
1012 : :
1013 : 1 : data.step = 0;
1014 : 1 : data.events = NULL;
1015 : :
1016 : : /* Create a file which exists and is not a directory. */
1017 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "testfilemonitor.db");
1018 : 1 : data.output_stream = g_file_replace (data.file, NULL, FALSE,
1019 : : G_FILE_CREATE_NONE, NULL, &error);
1020 : 1 : g_assert_no_error (error);
1021 : :
1022 : : /* Monitor it. Creating the monitor should not crash (bug #755721). */
1023 : 1 : data.monitor = g_file_monitor_file (data.file,
1024 : : G_FILE_MONITOR_WATCH_MOUNTS |
1025 : : G_FILE_MONITOR_WATCH_MOVES |
1026 : : G_FILE_MONITOR_WATCH_HARD_LINKS,
1027 : : NULL,
1028 : : &error);
1029 : 1 : g_assert_no_error (error);
1030 : 1 : g_assert_nonnull (data.monitor);
1031 : :
1032 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
1033 : :
1034 : : /* Change the file a bit. */
1035 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
1036 : 1 : g_signal_connect (data.monitor, "changed", (GCallback) monitor_changed, &data);
1037 : :
1038 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
1039 : 1 : g_timeout_add (500, file_hard_links_step, &data);
1040 : 1 : g_main_loop_run (data.loop);
1041 : :
1042 : 1 : check_expected_events (file_hard_links_output,
1043 : : G_N_ELEMENTS (file_hard_links_output),
1044 : : data.events,
1045 : : get_environment (data.monitor));
1046 : :
1047 : 1 : g_list_free_full (data.events, (GDestroyNotify) free_recorded_event);
1048 : 1 : g_main_loop_unref (data.loop);
1049 : 1 : g_object_unref (data.monitor);
1050 : 1 : g_object_unref (data.file);
1051 : 1 : g_object_unref (data.output_stream);
1052 : : }
1053 : :
1054 : : static void
1055 : 1 : test_finalize_in_callback (Fixture *fixture,
1056 : : gconstpointer user_data)
1057 : : {
1058 : 1 : GFile *file = NULL;
1059 : : guint i;
1060 : :
1061 : 1 : g_test_summary ("Test that finalization of a GFileMonitor in one of its "
1062 : : "callbacks doesn’t cause a deadlock.");
1063 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1941");
1064 : :
1065 : 1 : file = g_file_get_child (fixture->tmp_dir, "race-file");
1066 : :
1067 : 51 : for (i = 0; i < 50; i++)
1068 : : {
1069 : 50 : GFileMonitor *monitor = NULL;
1070 : 50 : GError *local_error = NULL;
1071 : :
1072 : : /* Monitor the file. */
1073 : 50 : monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
1074 : 50 : g_assert_no_error (local_error);
1075 : 50 : g_assert_nonnull (monitor);
1076 : :
1077 : : /* Create the file. */
1078 : 50 : g_file_replace_contents (file, "hello", 5, NULL, FALSE,
1079 : : G_FILE_CREATE_NONE, NULL, NULL, &local_error);
1080 : 50 : g_assert_no_error (local_error);
1081 : :
1082 : : /* Immediately drop the last ref to the monitor in the hope that this
1083 : : * happens in the middle of the critical section in
1084 : : * g_file_monitor_source_handle_event(), so that any cleanup at the end
1085 : : * of that function is done with a now-finalised file monitor. */
1086 : 50 : g_object_unref (monitor);
1087 : :
1088 : : /* Re-create the monitor and do the same again for deleting the file, to
1089 : : * give a second chance at hitting the race condition. */
1090 : 50 : monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
1091 : 50 : g_assert_no_error (local_error);
1092 : 50 : g_assert_nonnull (monitor);
1093 : :
1094 : : /* Delete the file. */
1095 : 50 : g_file_delete (file, NULL, &local_error);
1096 : 50 : g_assert_no_error (local_error);
1097 : :
1098 : : /* Drop the ref again. */
1099 : 50 : g_object_unref (monitor);
1100 : : }
1101 : :
1102 : 1 : g_object_unref (file);
1103 : 1 : }
1104 : :
1105 : : static void
1106 : 1 : test_root (Fixture *fixture,
1107 : : gconstpointer user_data)
1108 : : {
1109 : 1 : GFile *file = NULL;
1110 : 1 : GFileMonitor *monitor = NULL;
1111 : 1 : GError *local_error = NULL;
1112 : :
1113 : 1 : g_test_summary ("Test that GFileMonitor can monitor the root directory.");
1114 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3241");
1115 : :
1116 : : #if defined(G_OS_UNIX)
1117 : 1 : file = g_file_new_for_path ("/");
1118 : : #elif defined(G_OS_WIN32)
1119 : : file = g_file_new_for_path ("C:\\");
1120 : : #else
1121 : : g_test_skip ("Unsupported root directory");
1122 : : return;
1123 : : #endif
1124 : :
1125 : : /* We can’t test for any monitor events, but we can at least check that this
1126 : : * doesn’t crash or error. */
1127 : 1 : monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &local_error);
1128 : 1 : g_assert_no_error (local_error);
1129 : 1 : g_assert_nonnull (monitor);
1130 : :
1131 : 1 : g_clear_object (&monitor);
1132 : 1 : g_clear_object (&file);
1133 : 1 : }
1134 : :
1135 : : int
1136 : 1 : main (int argc, char *argv[])
1137 : : {
1138 : 1 : g_test_init (&argc, &argv, NULL);
1139 : :
1140 : 1 : g_test_add ("/monitor/atomic-replace", Fixture, NULL, setup, test_atomic_replace, teardown);
1141 : 1 : g_test_add ("/monitor/file-changes", Fixture, NULL, setup, test_file_changes, teardown);
1142 : 1 : g_test_add ("/monitor/dir-monitor", Fixture, NULL, setup, test_dir_monitor, teardown);
1143 : 1 : g_test_add ("/monitor/dir-not-existent", Fixture, NULL, setup, test_dir_non_existent, teardown);
1144 : 1 : g_test_add ("/monitor/cross-dir-moves", Fixture, NULL, setup, test_cross_dir_moves, teardown);
1145 : 1 : g_test_add ("/monitor/file/hard-links", Fixture, NULL, setup, test_file_hard_links, teardown);
1146 : 1 : g_test_add ("/monitor/finalize-in-callback", Fixture, NULL, setup, test_finalize_in_callback, teardown);
1147 : 1 : g_test_add ("/monitor/root", Fixture, NULL, setup, test_root, teardown);
1148 : :
1149 : 1 : return g_test_run ();
1150 : : }
|