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 : : } 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 : : /* If an event is marked as optional in the current environment and
241 : : * the event doesn't match, it means the expected event has lost. */
242 : 2 : else if (env & e1->optional)
243 : : {
244 : 2 : g_test_message ("Event %d at expected index %" G_GSIZE_FORMAT " skipped because "
245 : : "it is marked as optional", e1->event_type, i);
246 : 2 : i++;
247 : 2 : continue;
248 : : }
249 : : /* Run above checks under g_assert_* again to provide more useful
250 : : * error messages. Print the expected and actual events first. */
251 : : else
252 : : {
253 : : GList *ll;
254 : : gsize j;
255 : :
256 : 0 : g_test_message ("Recorded events:");
257 : 0 : for (ll = recorded; ll != NULL; ll = ll->next)
258 : 0 : output_event ((RecordedEvent *) ll->data);
259 : :
260 : 0 : g_test_message ("Expected events:");
261 : 0 : for (j = 0; j < n_expected; j++)
262 : 0 : output_event (&expected[j]);
263 : :
264 : 0 : g_assert_cmpint (e1->step, ==, e2->step);
265 : 0 : g_assert_cmpint (e1->event_type, ==, e2->event_type);
266 : :
267 : 0 : if (e1->file != DONT_CARE)
268 : 0 : g_assert_cmpstr (e1->file, ==, e2->file);
269 : :
270 : 0 : if (e1->other_file != DONT_CARE)
271 : 0 : g_assert_cmpstr (e1->other_file, ==, e2->other_file);
272 : :
273 : : g_assert_not_reached ();
274 : : }
275 : : }
276 : :
277 : 70 : i++, li++, l = l->next;
278 : 70 : if (l_extra_step)
279 : 0 : li++, l = l->next;
280 : : }
281 : :
282 : 7 : g_assert_cmpint (i, ==, n_expected);
283 : 7 : g_assert_cmpint (li, ==, g_list_length (recorded));
284 : 7 : }
285 : :
286 : : static void
287 : 70 : record_event (TestData *data,
288 : : gint event_type,
289 : : const gchar *file,
290 : : const gchar *other_file,
291 : : gint step)
292 : : {
293 : : RecordedEvent *event;
294 : :
295 : 70 : event = g_new0 (RecordedEvent, 1);
296 : 70 : event->event_type = event_type;
297 : 70 : event->file = g_strdup (file);
298 : 70 : event->other_file = g_strdup (other_file);
299 : 70 : event->step = step;
300 : :
301 : 70 : data->events = g_list_append (data->events, event);
302 : 70 : }
303 : :
304 : : static void
305 : 35 : monitor_changed (GFileMonitor *monitor,
306 : : GFile *file,
307 : : GFile *other_file,
308 : : GFileMonitorEvent event_type,
309 : : gpointer user_data)
310 : : {
311 : 35 : TestData *data = user_data;
312 : : gchar *basename, *other_base;
313 : :
314 : 35 : basename = g_file_get_basename (file);
315 : 35 : if (other_file)
316 : 4 : other_base = g_file_get_basename (other_file);
317 : : else
318 : 31 : other_base = NULL;
319 : :
320 : 35 : record_event (data, event_type, basename, other_base, -1);
321 : :
322 : 35 : g_free (basename);
323 : 35 : g_free (other_base);
324 : 35 : }
325 : :
326 : : static gboolean
327 : 4 : atomic_replace_step (gpointer user_data)
328 : : {
329 : 4 : TestData *data = user_data;
330 : 4 : GError *error = NULL;
331 : :
332 : 4 : switch (data->step)
333 : : {
334 : 1 : case 0:
335 : 1 : record_event (data, -1, NULL, NULL, 0);
336 : 1 : g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
337 : 1 : g_assert_no_error (error);
338 : 1 : break;
339 : 1 : case 1:
340 : 1 : record_event (data, -1, NULL, NULL, 1);
341 : 1 : g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
342 : 1 : g_assert_no_error (error);
343 : 1 : break;
344 : 1 : case 2:
345 : 1 : record_event (data, -1, NULL, NULL, 2);
346 : 1 : g_file_delete (data->file, NULL, NULL);
347 : 1 : break;
348 : 1 : case 3:
349 : 1 : record_event (data, -1, NULL, NULL, 3);
350 : 1 : g_main_loop_quit (data->loop);
351 : 1 : return G_SOURCE_REMOVE;
352 : : }
353 : :
354 : 3 : data->step++;
355 : :
356 : 3 : return G_SOURCE_CONTINUE;
357 : : }
358 : :
359 : : /* this is the output we expect from the above steps */
360 : : static RecordedEvent atomic_replace_output[] = {
361 : : { -1, NULL, NULL, 0, NONE },
362 : : { G_FILE_MONITOR_EVENT_CREATED, "atomic_replace_file", NULL, -1, NONE },
363 : : { G_FILE_MONITOR_EVENT_CHANGED, "atomic_replace_file", NULL, -1, KQUEUE },
364 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "atomic_replace_file", NULL, -1, KQUEUE },
365 : : { -1, NULL, NULL, 1, NONE },
366 : : { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE, "atomic_replace_file", -1, NONE },
367 : : { -1, NULL, NULL, 2, NONE },
368 : : { G_FILE_MONITOR_EVENT_DELETED, "atomic_replace_file", NULL, -1, NONE },
369 : : { -1, NULL, NULL, 3, NONE }
370 : : };
371 : :
372 : : static void
373 : 1 : test_atomic_replace (Fixture *fixture,
374 : : gconstpointer user_data)
375 : : {
376 : 1 : GError *error = NULL;
377 : : TestData data;
378 : :
379 : 1 : if (skip_win32 ())
380 : 0 : return;
381 : :
382 : 1 : data.step = 0;
383 : 1 : data.events = NULL;
384 : :
385 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "atomic_replace_file");
386 : 1 : g_file_delete (data.file, NULL, NULL);
387 : :
388 : 1 : data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
389 : 1 : g_assert_no_error (error);
390 : :
391 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
392 : :
393 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
394 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
395 : :
396 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
397 : :
398 : 1 : g_timeout_add (500, atomic_replace_step, &data);
399 : :
400 : 1 : g_main_loop_run (data.loop);
401 : :
402 : 1 : check_expected_events (atomic_replace_output,
403 : : G_N_ELEMENTS (atomic_replace_output),
404 : : data.events,
405 : : get_environment (data.monitor));
406 : :
407 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
408 : 1 : g_main_loop_unref (data.loop);
409 : 1 : g_object_unref (data.monitor);
410 : 1 : g_object_unref (data.file);
411 : : }
412 : :
413 : : static gboolean
414 : 5 : change_step (gpointer user_data)
415 : : {
416 : 5 : TestData *data = user_data;
417 : : GOutputStream *stream;
418 : 5 : GError *error = NULL;
419 : 5 : guint32 mode = 0660;
420 : :
421 : 5 : switch (data->step)
422 : : {
423 : 1 : case 0:
424 : 1 : record_event (data, -1, NULL, NULL, 0);
425 : 1 : g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
426 : 1 : g_assert_no_error (error);
427 : 1 : break;
428 : 1 : case 1:
429 : 1 : record_event (data, -1, NULL, NULL, 1);
430 : 1 : stream = (GOutputStream *)g_file_append_to (data->file, G_FILE_CREATE_NONE, NULL, &error);
431 : 1 : g_assert_no_error (error);
432 : 1 : g_output_stream_write_all (stream, " step 1", 7, NULL, NULL, &error);
433 : 1 : g_assert_no_error (error);
434 : 1 : g_output_stream_close (stream, NULL, &error);
435 : 1 : g_assert_no_error (error);
436 : 1 : g_object_unref (stream);
437 : 1 : break;
438 : 1 : case 2:
439 : 1 : record_event (data, -1, NULL, NULL, 2);
440 : 1 : g_file_set_attribute (data->file,
441 : : G_FILE_ATTRIBUTE_UNIX_MODE,
442 : : G_FILE_ATTRIBUTE_TYPE_UINT32,
443 : : &mode,
444 : : G_FILE_QUERY_INFO_NONE,
445 : : NULL,
446 : : &error);
447 : 1 : g_assert_no_error (error);
448 : 1 : break;
449 : 1 : case 3:
450 : 1 : record_event (data, -1, NULL, NULL, 3);
451 : 1 : g_file_delete (data->file, NULL, NULL);
452 : 1 : break;
453 : 1 : case 4:
454 : 1 : record_event (data, -1, NULL, NULL, 4);
455 : 1 : g_main_loop_quit (data->loop);
456 : 1 : return G_SOURCE_REMOVE;
457 : : }
458 : :
459 : 4 : data->step++;
460 : :
461 : 4 : return G_SOURCE_CONTINUE;
462 : : }
463 : :
464 : : /* this is the output we expect from the above steps */
465 : : static RecordedEvent change_output[] = {
466 : : { -1, NULL, NULL, 0, NONE },
467 : : { G_FILE_MONITOR_EVENT_CREATED, "change_file", NULL, -1, NONE },
468 : : { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, KQUEUE },
469 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, KQUEUE },
470 : : { -1, NULL, NULL, 1, NONE },
471 : : { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, NONE },
472 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, NONE },
473 : : { -1, NULL, NULL, 2, NONE },
474 : : { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, "change_file", NULL, -1, NONE },
475 : : { -1, NULL, NULL, 3, NONE },
476 : : { G_FILE_MONITOR_EVENT_DELETED, "change_file", NULL, -1, NONE },
477 : : { -1, NULL, NULL, 4, NONE }
478 : : };
479 : :
480 : : static void
481 : 1 : test_file_changes (Fixture *fixture,
482 : : gconstpointer user_data)
483 : : {
484 : 1 : GError *error = NULL;
485 : : TestData data;
486 : :
487 : 1 : if (skip_win32 ())
488 : 0 : return;
489 : :
490 : 1 : data.step = 0;
491 : 1 : data.events = NULL;
492 : :
493 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "change_file");
494 : 1 : g_file_delete (data.file, NULL, NULL);
495 : :
496 : 1 : data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
497 : 1 : g_assert_no_error (error);
498 : :
499 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
500 : :
501 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
502 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
503 : :
504 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
505 : :
506 : 1 : g_timeout_add (500, change_step, &data);
507 : :
508 : 1 : g_main_loop_run (data.loop);
509 : :
510 : 1 : check_expected_events (change_output,
511 : : G_N_ELEMENTS (change_output),
512 : : data.events,
513 : : get_environment (data.monitor));
514 : :
515 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
516 : 1 : g_main_loop_unref (data.loop);
517 : 1 : g_object_unref (data.monitor);
518 : 1 : g_object_unref (data.file);
519 : : }
520 : :
521 : : static gboolean
522 : 7 : dir_step (gpointer user_data)
523 : : {
524 : 7 : TestData *data = user_data;
525 : : GFile *parent, *file, *file2;
526 : 7 : GError *error = NULL;
527 : :
528 : 7 : switch (data->step)
529 : : {
530 : 1 : case 1:
531 : 1 : record_event (data, -1, NULL, NULL, 1);
532 : 1 : parent = g_file_get_parent (data->file);
533 : 1 : file = g_file_get_child (parent, "dir_test_file");
534 : 1 : g_file_replace_contents (file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
535 : 1 : g_assert_no_error (error);
536 : 1 : g_object_unref (file);
537 : 1 : g_object_unref (parent);
538 : 1 : break;
539 : 1 : case 2:
540 : 1 : record_event (data, -1, NULL, NULL, 2);
541 : 1 : parent = g_file_get_parent (data->file);
542 : 1 : file = g_file_get_child (parent, "dir_test_file");
543 : 1 : file2 = g_file_get_child (data->file, "dir_test_file");
544 : 1 : g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
545 : 1 : g_assert_no_error (error);
546 : 1 : g_object_unref (file);
547 : 1 : g_object_unref (file2);
548 : 1 : g_object_unref (parent);
549 : 1 : break;
550 : 1 : case 3:
551 : 1 : record_event (data, -1, NULL, NULL, 3);
552 : 1 : file = g_file_get_child (data->file, "dir_test_file");
553 : 1 : file2 = g_file_get_child (data->file, "dir_test_file2");
554 : 1 : g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
555 : 1 : g_assert_no_error (error);
556 : 1 : g_object_unref (file);
557 : 1 : g_object_unref (file2);
558 : 1 : break;
559 : 1 : case 4:
560 : 1 : record_event (data, -1, NULL, NULL, 4);
561 : 1 : parent = g_file_get_parent (data->file);
562 : 1 : file = g_file_get_child (data->file, "dir_test_file2");
563 : 1 : file2 = g_file_get_child (parent, "dir_test_file2");
564 : 1 : g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
565 : 1 : g_assert_no_error (error);
566 : 1 : g_file_delete (file2, NULL, NULL);
567 : 1 : g_object_unref (file);
568 : 1 : g_object_unref (file2);
569 : 1 : g_object_unref (parent);
570 : 1 : break;
571 : 1 : case 5:
572 : 1 : record_event (data, -1, NULL, NULL, 5);
573 : 1 : g_file_delete (data->file, NULL, NULL);
574 : 1 : break;
575 : 1 : case 6:
576 : 1 : record_event (data, -1, NULL, NULL, 6);
577 : 1 : g_main_loop_quit (data->loop);
578 : 1 : return G_SOURCE_REMOVE;
579 : : }
580 : :
581 : 6 : data->step++;
582 : :
583 : 6 : return G_SOURCE_CONTINUE;
584 : : }
585 : :
586 : : /* this is the output we expect from the above steps */
587 : : static RecordedEvent dir_output[] = {
588 : : { -1, NULL, NULL, 1, NONE },
589 : : { -1, NULL, NULL, 2, NONE },
590 : : { G_FILE_MONITOR_EVENT_MOVED_IN, "dir_test_file", NULL, -1, NONE },
591 : : { -1, NULL, NULL, 3, NONE },
592 : : { G_FILE_MONITOR_EVENT_RENAMED, "dir_test_file", "dir_test_file2", -1, NONE },
593 : : { -1, NULL, NULL, 4, NONE },
594 : : { G_FILE_MONITOR_EVENT_MOVED_OUT, "dir_test_file2", NULL, -1, NONE },
595 : : { -1, NULL, NULL, 5, NONE },
596 : : { G_FILE_MONITOR_EVENT_DELETED, "dir_monitor_test", NULL, -1, NONE },
597 : : { -1, NULL, NULL, 6, NONE }
598 : : };
599 : :
600 : : static void
601 : 1 : test_dir_monitor (Fixture *fixture,
602 : : gconstpointer user_data)
603 : : {
604 : 1 : GError *error = NULL;
605 : : TestData data;
606 : :
607 : 1 : if (skip_win32 ())
608 : 0 : return;
609 : :
610 : 1 : data.step = 0;
611 : 1 : data.events = NULL;
612 : :
613 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "dir_monitor_test");
614 : 1 : g_file_delete (data.file, NULL, NULL);
615 : 1 : g_file_make_directory (data.file, NULL, &error);
616 : :
617 : 1 : data.monitor = g_file_monitor_directory (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
618 : 1 : g_assert_no_error (error);
619 : :
620 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
621 : :
622 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
623 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
624 : :
625 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
626 : :
627 : 1 : g_timeout_add (500, dir_step, &data);
628 : :
629 : 1 : g_main_loop_run (data.loop);
630 : :
631 : 1 : check_expected_events (dir_output,
632 : : G_N_ELEMENTS (dir_output),
633 : : data.events,
634 : : get_environment (data.monitor));
635 : :
636 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
637 : 1 : g_main_loop_unref (data.loop);
638 : 1 : g_object_unref (data.monitor);
639 : 1 : g_object_unref (data.file);
640 : : }
641 : :
642 : : static gboolean
643 : 5 : nodir_step (gpointer user_data)
644 : : {
645 : 5 : TestData *data = user_data;
646 : : GFile *parent;
647 : 5 : GError *error = NULL;
648 : :
649 : 5 : switch (data->step)
650 : : {
651 : 1 : case 0:
652 : 1 : record_event (data, -1, NULL, NULL, 0);
653 : 1 : parent = g_file_get_parent (data->file);
654 : 1 : g_file_make_directory (parent, NULL, &error);
655 : 1 : g_assert_no_error (error);
656 : 1 : g_object_unref (parent);
657 : 1 : break;
658 : 1 : case 1:
659 : 1 : record_event (data, -1, NULL, NULL, 1);
660 : 1 : g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
661 : 1 : g_assert_no_error (error);
662 : 1 : break;
663 : 1 : case 2:
664 : 1 : record_event (data, -1, NULL, NULL, 2);
665 : 1 : g_file_delete (data->file, NULL, &error);
666 : 1 : g_assert_no_error (error);
667 : 1 : break;
668 : 1 : case 3:
669 : 1 : record_event (data, -1, NULL, NULL, 3);
670 : 1 : parent = g_file_get_parent (data->file);
671 : 1 : g_file_delete (parent, NULL, &error);
672 : 1 : g_assert_no_error (error);
673 : 1 : g_object_unref (parent);
674 : 1 : break;
675 : 1 : case 4:
676 : 1 : record_event (data, -1, NULL, NULL, 4);
677 : 1 : g_main_loop_quit (data->loop);
678 : 1 : return G_SOURCE_REMOVE;
679 : : }
680 : :
681 : 4 : data->step++;
682 : :
683 : 4 : return G_SOURCE_CONTINUE;
684 : : }
685 : :
686 : : static RecordedEvent nodir_output[] = {
687 : : { -1, NULL, NULL, 0, NONE },
688 : : { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, KQUEUE },
689 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
690 : : { -1, NULL, NULL, 1, NONE },
691 : : { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, NONE },
692 : : { G_FILE_MONITOR_EVENT_CHANGED, "nosuchfile", NULL, -1, KQUEUE },
693 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
694 : : { -1, NULL, NULL, 2, NONE },
695 : : { G_FILE_MONITOR_EVENT_DELETED, "nosuchfile", NULL, -1, NONE },
696 : : { -1, NULL, NULL, 3, NONE },
697 : : { -1, NULL, NULL, 4, NONE }
698 : : };
699 : :
700 : : static void
701 : 1 : test_dir_non_existent (Fixture *fixture,
702 : : gconstpointer user_data)
703 : : {
704 : : TestData data;
705 : 1 : GError *error = NULL;
706 : :
707 : 1 : if (skip_win32 ())
708 : 0 : return;
709 : :
710 : 1 : data.step = 0;
711 : 1 : data.events = NULL;
712 : :
713 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "nosuchdir/nosuchfile");
714 : 1 : data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
715 : 1 : g_assert_no_error (error);
716 : :
717 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
718 : :
719 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
720 : 1 : g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
721 : :
722 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
723 : :
724 : : /* we need a long timeout here, since the inotify implementation only scans
725 : : * for missing files every 4 seconds.
726 : : */
727 : 1 : g_timeout_add (5000, nodir_step, &data);
728 : :
729 : 1 : g_main_loop_run (data.loop);
730 : :
731 : 1 : check_expected_events (nodir_output,
732 : : G_N_ELEMENTS (nodir_output),
733 : : data.events,
734 : : get_environment (data.monitor));
735 : :
736 : 1 : g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
737 : 1 : g_main_loop_unref (data.loop);
738 : 1 : g_object_unref (data.monitor);
739 : 1 : g_object_unref (data.file);
740 : : }
741 : :
742 : : static gboolean
743 : 4 : cross_dir_step (gpointer user_data)
744 : : {
745 : 4 : TestData *data = user_data;
746 : : GFile *file, *file2;
747 : 4 : GError *error = NULL;
748 : :
749 : 4 : switch (data[0].step)
750 : : {
751 : 1 : case 0:
752 : 1 : record_event (&data[0], -1, NULL, NULL, 0);
753 : 1 : record_event (&data[1], -1, NULL, NULL, 0);
754 : 1 : file = g_file_get_child (data[1].file, "a");
755 : 1 : g_file_replace_contents (file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
756 : 1 : g_assert_no_error (error);
757 : 1 : g_object_unref (file);
758 : 1 : break;
759 : 1 : case 1:
760 : 1 : record_event (&data[0], -1, NULL, NULL, 1);
761 : 1 : record_event (&data[1], -1, NULL, NULL, 1);
762 : 1 : file = g_file_get_child (data[1].file, "a");
763 : 1 : file2 = g_file_get_child (data[0].file, "a");
764 : 1 : g_file_move (file, file2, 0, NULL, NULL, NULL, &error);
765 : 1 : g_assert_no_error (error);
766 : 1 : g_object_unref (file);
767 : 1 : g_object_unref (file2);
768 : 1 : break;
769 : 1 : case 2:
770 : 1 : record_event (&data[0], -1, NULL, NULL, 2);
771 : 1 : record_event (&data[1], -1, NULL, NULL, 2);
772 : 1 : file2 = g_file_get_child (data[0].file, "a");
773 : 1 : g_file_delete (file2, NULL, NULL);
774 : 1 : g_file_delete (data[0].file, NULL, NULL);
775 : 1 : g_file_delete (data[1].file, NULL, NULL);
776 : 1 : g_object_unref (file2);
777 : 1 : break;
778 : 1 : case 3:
779 : 1 : record_event (&data[0], -1, NULL, NULL, 3);
780 : 1 : record_event (&data[1], -1, NULL, NULL, 3);
781 : 1 : g_main_loop_quit (data->loop);
782 : 1 : return G_SOURCE_REMOVE;
783 : : }
784 : :
785 : 3 : data->step++;
786 : :
787 : 3 : return G_SOURCE_CONTINUE;
788 : : }
789 : :
790 : : static RecordedEvent cross_dir_a_output[] = {
791 : : { -1, NULL, NULL, 0, NONE },
792 : : { -1, NULL, NULL, 1, NONE },
793 : : { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
794 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
795 : : { -1, NULL, NULL, 2, NONE },
796 : : { G_FILE_MONITOR_EVENT_DELETED, "a", NULL, -1, NONE },
797 : : { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_a", NULL, -1, NONE },
798 : : { -1, NULL, NULL, 3, NONE },
799 : : };
800 : :
801 : : static RecordedEvent cross_dir_b_output[] = {
802 : : { -1, NULL, NULL, 0, NONE },
803 : : { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
804 : : { G_FILE_MONITOR_EVENT_CHANGED, "a", NULL, -1, KQUEUE },
805 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
806 : : { -1, NULL, NULL, 1, NONE },
807 : : { G_FILE_MONITOR_EVENT_MOVED_OUT, "a", "a", -1, NONE },
808 : : { -1, NULL, NULL, 2, NONE },
809 : : { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_b", NULL, -1, NONE },
810 : : { -1, NULL, NULL, 3, NONE },
811 : : };
812 : : static void
813 : 1 : test_cross_dir_moves (Fixture *fixture,
814 : : gconstpointer user_data)
815 : : {
816 : 1 : GError *error = NULL;
817 : : TestData data[2];
818 : :
819 : 1 : if (skip_win32 ())
820 : 0 : return;
821 : :
822 : 1 : data[0].step = 0;
823 : 1 : data[0].events = NULL;
824 : :
825 : 1 : data[0].file = g_file_get_child (fixture->tmp_dir, "cross_dir_a");
826 : 1 : g_file_delete (data[0].file, NULL, NULL);
827 : 1 : g_file_make_directory (data[0].file, NULL, &error);
828 : :
829 : 1 : data[0].monitor = g_file_monitor_directory (data[0].file, 0, NULL, &error);
830 : 1 : g_assert_no_error (error);
831 : :
832 : 1 : g_test_message ("Using GFileMonitor 0 %s", G_OBJECT_TYPE_NAME (data[0].monitor));
833 : :
834 : 1 : g_file_monitor_set_rate_limit (data[0].monitor, 200);
835 : 1 : g_signal_connect (data[0].monitor, "changed", G_CALLBACK (monitor_changed), &data[0]);
836 : :
837 : 1 : data[1].step = 0;
838 : 1 : data[1].events = NULL;
839 : :
840 : 1 : data[1].file = g_file_get_child (fixture->tmp_dir, "cross_dir_b");
841 : 1 : g_file_delete (data[1].file, NULL, NULL);
842 : 1 : g_file_make_directory (data[1].file, NULL, &error);
843 : :
844 : 1 : data[1].monitor = g_file_monitor_directory (data[1].file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
845 : 1 : g_assert_no_error (error);
846 : :
847 : 1 : g_test_message ("Using GFileMonitor 1 %s", G_OBJECT_TYPE_NAME (data[1].monitor));
848 : :
849 : 1 : g_file_monitor_set_rate_limit (data[1].monitor, 200);
850 : 1 : g_signal_connect (data[1].monitor, "changed", G_CALLBACK (monitor_changed), &data[1]);
851 : :
852 : 1 : data[0].loop = g_main_loop_new (NULL, TRUE);
853 : :
854 : 1 : g_timeout_add (500, cross_dir_step, data);
855 : :
856 : 1 : g_main_loop_run (data[0].loop);
857 : :
858 : 1 : check_expected_events (cross_dir_a_output,
859 : : G_N_ELEMENTS (cross_dir_a_output),
860 : : data[0].events,
861 : : get_environment (data[0].monitor));
862 : 1 : check_expected_events (cross_dir_b_output,
863 : : G_N_ELEMENTS (cross_dir_b_output),
864 : : data[1].events,
865 : : get_environment (data[1].monitor));
866 : :
867 : 1 : g_list_free_full (data[0].events, (GDestroyNotify)free_recorded_event);
868 : 1 : g_main_loop_unref (data[0].loop);
869 : 1 : g_object_unref (data[0].monitor);
870 : 1 : g_object_unref (data[0].file);
871 : :
872 : 1 : g_list_free_full (data[1].events, (GDestroyNotify)free_recorded_event);
873 : 1 : g_object_unref (data[1].monitor);
874 : 1 : g_object_unref (data[1].file);
875 : : }
876 : :
877 : : static gboolean
878 : 7 : file_hard_links_step (gpointer user_data)
879 : : {
880 : 7 : gboolean retval = G_SOURCE_CONTINUE;
881 : 7 : TestData *data = user_data;
882 : 7 : GError *error = NULL;
883 : :
884 : 7 : gchar *filename = g_file_get_path (data->file);
885 : 7 : gchar *hard_link_name = g_strdup_printf ("%s2", filename);
886 : 7 : GFile *hard_link_file = g_file_new_for_path (hard_link_name);
887 : :
888 : 7 : switch (data->step)
889 : : {
890 : 1 : case 0:
891 : 1 : record_event (data, -1, NULL, NULL, 0);
892 : 1 : g_output_stream_write_all (G_OUTPUT_STREAM (data->output_stream),
893 : : "hello, step 0", 13, NULL, NULL, &error);
894 : 1 : g_assert_no_error (error);
895 : 1 : g_output_stream_close (G_OUTPUT_STREAM (data->output_stream), NULL, &error);
896 : 1 : g_assert_no_error (error);
897 : 1 : break;
898 : 1 : case 1:
899 : 1 : record_event (data, -1, NULL, NULL, 1);
900 : 1 : g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE,
901 : : G_FILE_CREATE_NONE, NULL, NULL, &error);
902 : 1 : g_assert_no_error (error);
903 : 1 : break;
904 : 1 : case 2:
905 : 1 : record_event (data, -1, NULL, NULL, 2);
906 : : #ifdef HAVE_LINK
907 : 1 : if (link (filename, hard_link_name) < 0)
908 : : {
909 : 0 : g_error ("link(%s, %s) failed: %s", filename, hard_link_name, g_strerror (errno));
910 : : }
911 : : #endif /* HAVE_LINK */
912 : 1 : break;
913 : 1 : case 3:
914 : 1 : record_event (data, -1, NULL, NULL, 3);
915 : : #ifdef HAVE_LINK
916 : : {
917 : 1 : GOutputStream *hard_link_stream = NULL;
918 : :
919 : : /* Deliberately don’t do an atomic swap on the hard-linked file. */
920 : 1 : hard_link_stream = G_OUTPUT_STREAM (g_file_append_to (hard_link_file,
921 : : G_FILE_CREATE_NONE,
922 : : NULL, &error));
923 : 1 : g_assert_no_error (error);
924 : 1 : g_output_stream_write_all (hard_link_stream, " step 3", 7, NULL, NULL, &error);
925 : 1 : g_assert_no_error (error);
926 : 1 : g_output_stream_close (hard_link_stream, NULL, &error);
927 : 1 : g_assert_no_error (error);
928 : 1 : g_object_unref (hard_link_stream);
929 : : }
930 : : #endif /* HAVE_LINK */
931 : 1 : break;
932 : 1 : case 4:
933 : 1 : record_event (data, -1, NULL, NULL, 4);
934 : 1 : g_file_delete (data->file, NULL, &error);
935 : 1 : g_assert_no_error (error);
936 : 1 : break;
937 : 1 : case 5:
938 : 1 : record_event (data, -1, NULL, NULL, 5);
939 : : #ifdef HAVE_LINK
940 : 1 : g_file_delete (hard_link_file, NULL, &error);
941 : 1 : g_assert_no_error (error);
942 : : #endif /* HAVE_LINK */
943 : 1 : break;
944 : 1 : case 6:
945 : 1 : record_event (data, -1, NULL, NULL, 6);
946 : 1 : g_main_loop_quit (data->loop);
947 : 1 : retval = G_SOURCE_REMOVE;
948 : 1 : break;
949 : : }
950 : :
951 : 7 : if (retval != G_SOURCE_REMOVE)
952 : 6 : data->step++;
953 : :
954 : 7 : g_object_unref (hard_link_file);
955 : 7 : g_free (hard_link_name);
956 : 7 : g_free (filename);
957 : :
958 : 7 : return retval;
959 : : }
960 : :
961 : : static RecordedEvent file_hard_links_output[] = {
962 : : { -1, NULL, NULL, 0, NONE },
963 : : { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, NONE },
964 : : { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "testfilemonitor.db", NULL, -1, NONE },
965 : : { -1, NULL, NULL, 1, NONE },
966 : : { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE /* .goutputstream-XXXXXX */, "testfilemonitor.db", -1, NONE },
967 : : { -1, NULL, NULL, 2, NONE },
968 : : { -1, NULL, NULL, 3, NONE },
969 : : /* Kqueue is based on file descriptors. You can get events from all hard
970 : : * links by just monitoring one open file descriptor, and it is not possible
971 : : * to know whether it is done on the file name we use to open the file. Since
972 : : * the hard link count of 'testfilemonitor.db' is 2, it is expected to see
973 : : * two 'DELETED' events reported here. You have to call 'unlink' twice on
974 : : * different file names to remove 'testfilemonitor.db' from the file system,
975 : : * and each 'unlink' call generates a 'DELETED' event. */
976 : : { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, INOTIFY },
977 : : { -1, NULL, NULL, 4, NONE },
978 : : { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, NONE },
979 : : { -1, NULL, NULL, 5, NONE },
980 : : { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, INOTIFY },
981 : : { -1, NULL, NULL, 6, NONE },
982 : : };
983 : :
984 : : static void
985 : 1 : test_file_hard_links (Fixture *fixture,
986 : : gconstpointer user_data)
987 : : {
988 : 1 : GError *error = NULL;
989 : : TestData data;
990 : :
991 : 1 : g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755721");
992 : :
993 : 1 : if (skip_win32 ())
994 : 0 : return;
995 : :
996 : : #ifdef HAVE_LINK
997 : 1 : g_test_message ("Running with hard link tests");
998 : : #else /* if !HAVE_LINK */
999 : : g_test_message ("Running without hard link tests");
1000 : : #endif /* !HAVE_LINK */
1001 : :
1002 : 1 : data.step = 0;
1003 : 1 : data.events = NULL;
1004 : :
1005 : : /* Create a file which exists and is not a directory. */
1006 : 1 : data.file = g_file_get_child (fixture->tmp_dir, "testfilemonitor.db");
1007 : 1 : data.output_stream = g_file_replace (data.file, NULL, FALSE,
1008 : : G_FILE_CREATE_NONE, NULL, &error);
1009 : 1 : g_assert_no_error (error);
1010 : :
1011 : : /* Monitor it. Creating the monitor should not crash (bug #755721). */
1012 : 1 : data.monitor = g_file_monitor_file (data.file,
1013 : : G_FILE_MONITOR_WATCH_MOUNTS |
1014 : : G_FILE_MONITOR_WATCH_MOVES |
1015 : : G_FILE_MONITOR_WATCH_HARD_LINKS,
1016 : : NULL,
1017 : : &error);
1018 : 1 : g_assert_no_error (error);
1019 : 1 : g_assert_nonnull (data.monitor);
1020 : :
1021 : 1 : g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
1022 : :
1023 : : /* Change the file a bit. */
1024 : 1 : g_file_monitor_set_rate_limit (data.monitor, 200);
1025 : 1 : g_signal_connect (data.monitor, "changed", (GCallback) monitor_changed, &data);
1026 : :
1027 : 1 : data.loop = g_main_loop_new (NULL, TRUE);
1028 : 1 : g_timeout_add (500, file_hard_links_step, &data);
1029 : 1 : g_main_loop_run (data.loop);
1030 : :
1031 : 1 : check_expected_events (file_hard_links_output,
1032 : : G_N_ELEMENTS (file_hard_links_output),
1033 : : data.events,
1034 : : get_environment (data.monitor));
1035 : :
1036 : 1 : g_list_free_full (data.events, (GDestroyNotify) free_recorded_event);
1037 : 1 : g_main_loop_unref (data.loop);
1038 : 1 : g_object_unref (data.monitor);
1039 : 1 : g_object_unref (data.file);
1040 : 1 : g_object_unref (data.output_stream);
1041 : : }
1042 : :
1043 : : static void
1044 : 1 : test_finalize_in_callback (Fixture *fixture,
1045 : : gconstpointer user_data)
1046 : : {
1047 : 1 : GFile *file = NULL;
1048 : : guint i;
1049 : :
1050 : 1 : g_test_summary ("Test that finalization of a GFileMonitor in one of its "
1051 : : "callbacks doesn’t cause a deadlock.");
1052 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1941");
1053 : :
1054 : 1 : file = g_file_get_child (fixture->tmp_dir, "race-file");
1055 : :
1056 : 51 : for (i = 0; i < 50; i++)
1057 : : {
1058 : 50 : GFileMonitor *monitor = NULL;
1059 : 50 : GError *local_error = NULL;
1060 : :
1061 : : /* Monitor the file. */
1062 : 50 : monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
1063 : 50 : g_assert_no_error (local_error);
1064 : 50 : g_assert_nonnull (monitor);
1065 : :
1066 : : /* Create the file. */
1067 : 50 : g_file_replace_contents (file, "hello", 5, NULL, FALSE,
1068 : : G_FILE_CREATE_NONE, NULL, NULL, &local_error);
1069 : 50 : g_assert_no_error (local_error);
1070 : :
1071 : : /* Immediately drop the last ref to the monitor in the hope that this
1072 : : * happens in the middle of the critical section in
1073 : : * g_file_monitor_source_handle_event(), so that any cleanup at the end
1074 : : * of that function is done with a now-finalised file monitor. */
1075 : 50 : g_object_unref (monitor);
1076 : :
1077 : : /* Re-create the monitor and do the same again for deleting the file, to
1078 : : * give a second chance at hitting the race condition. */
1079 : 50 : monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
1080 : 50 : g_assert_no_error (local_error);
1081 : 50 : g_assert_nonnull (monitor);
1082 : :
1083 : : /* Delete the file. */
1084 : 50 : g_file_delete (file, NULL, &local_error);
1085 : 50 : g_assert_no_error (local_error);
1086 : :
1087 : : /* Drop the ref again. */
1088 : 50 : g_object_unref (monitor);
1089 : : }
1090 : :
1091 : 1 : g_object_unref (file);
1092 : 1 : }
1093 : :
1094 : : static void
1095 : 1 : test_root (Fixture *fixture,
1096 : : gconstpointer user_data)
1097 : : {
1098 : 1 : GFile *file = NULL;
1099 : 1 : GFileMonitor *monitor = NULL;
1100 : 1 : GError *local_error = NULL;
1101 : :
1102 : 1 : g_test_summary ("Test that GFileMonitor can monitor the root directory.");
1103 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3241");
1104 : :
1105 : : #if defined(G_OS_UNIX)
1106 : 1 : file = g_file_new_for_path ("/");
1107 : : #elif defined(G_OS_WIN32)
1108 : : file = g_file_new_for_path ("C:\\");
1109 : : #else
1110 : : g_test_skip ("Unsupported root directory");
1111 : : return;
1112 : : #endif
1113 : :
1114 : : /* We can’t test for any monitor events, but we can at least check that this
1115 : : * doesn’t crash or error. */
1116 : 1 : monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &local_error);
1117 : 1 : g_assert_no_error (local_error);
1118 : 1 : g_assert_nonnull (monitor);
1119 : :
1120 : 1 : g_clear_object (&monitor);
1121 : 1 : g_clear_object (&file);
1122 : 1 : }
1123 : :
1124 : : int
1125 : 1 : main (int argc, char *argv[])
1126 : : {
1127 : 1 : g_test_init (&argc, &argv, NULL);
1128 : :
1129 : 1 : g_test_add ("/monitor/atomic-replace", Fixture, NULL, setup, test_atomic_replace, teardown);
1130 : 1 : g_test_add ("/monitor/file-changes", Fixture, NULL, setup, test_file_changes, teardown);
1131 : 1 : g_test_add ("/monitor/dir-monitor", Fixture, NULL, setup, test_dir_monitor, teardown);
1132 : 1 : g_test_add ("/monitor/dir-not-existent", Fixture, NULL, setup, test_dir_non_existent, teardown);
1133 : 1 : g_test_add ("/monitor/cross-dir-moves", Fixture, NULL, setup, test_cross_dir_moves, teardown);
1134 : 1 : g_test_add ("/monitor/file/hard-links", Fixture, NULL, setup, test_file_hard_links, teardown);
1135 : 1 : g_test_add ("/monitor/finalize-in-callback", Fixture, NULL, setup, test_finalize_in_callback, teardown);
1136 : 1 : g_test_add ("/monitor/root", Fixture, NULL, setup, test_root, teardown);
1137 : :
1138 : 1 : return g_test_run ();
1139 : : }
|