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