| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | #include "config.h"
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #include <errno.h>
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  |  | #include <gio/gio.h>
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  | static gboolean | 
					
						
							|  |  |  |  | skip_win32 (void) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  | #ifdef G_OS_WIN32
 | 
					
						
							|  |  |  |  |   g_test_skip ("FIXME, test is broken on win32"); | 
					
						
							|  |  |  |  |   return TRUE; | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |   return FALSE; | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  | /* These tests were written for the inotify implementation.
 | 
					
						
							|  |  |  |  |  * Other implementations may require slight adjustments in | 
					
						
							|  |  |  |  |  * the tests, e.g. the length of timeouts | 
					
						
							|  |  |  |  |  */ | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | typedef struct | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFile *tmp_dir; | 
					
						
							|  |  |  |  | } Fixture; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | setup (Fixture       *fixture, | 
					
						
							|  |  |  |  |        gconstpointer  user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   gchar *path = NULL; | 
					
						
							|  |  |  |  |   GError *local_error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   path = g_dir_make_tmp ("gio-test-testfilemonitor_XXXXXX", &local_error); | 
					
						
							|  |  |  |  |   g_assert_no_error (local_error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   fixture->tmp_dir = g_file_new_for_path (path); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_test_message ("Using temporary directory: %s", path); | 
					
						
							| 
									
										
										
										
											2019-10-16 11:33:58 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_free (path); | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | teardown (Fixture       *fixture, | 
					
						
							|  |  |  |  |           gconstpointer  user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GError *local_error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_file_delete (fixture->tmp_dir, NULL, &local_error); | 
					
						
							|  |  |  |  |   g_assert_no_error (local_error); | 
					
						
							|  |  |  |  |   g_clear_object (&fixture->tmp_dir); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  | typedef enum { | 
					
						
							|  |  |  |  |   NONE      = 0, | 
					
						
							|  |  |  |  |   INOTIFY   = (1 << 1), | 
					
						
							|  |  |  |  |   KQUEUE    = (1 << 2) | 
					
						
							|  |  |  |  | } Environment; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | typedef struct | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   gint event_type; | 
					
						
							|  |  |  |  |   gchar *file; | 
					
						
							|  |  |  |  |   gchar *other_file; | 
					
						
							|  |  |  |  |   gint step; | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Since different file monitor implementation has different capabilities,
 | 
					
						
							|  |  |  |  |    * we cannot expect all implementations to report all kind of events without | 
					
						
							|  |  |  |  |    * any loss. This 'optional' field is a bit mask used to mark events which | 
					
						
							|  |  |  |  |    * may be lost under specific platforms. | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   Environment optional; | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | } RecordedEvent; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | free_recorded_event (RecordedEvent *event) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_free (event->file); | 
					
						
							|  |  |  |  |   g_free (event->other_file); | 
					
						
							|  |  |  |  |   g_free (event); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | typedef struct | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFile *file; | 
					
						
							|  |  |  |  |   GFileMonitor *monitor; | 
					
						
							|  |  |  |  |   GMainLoop *loop; | 
					
						
							|  |  |  |  |   gint step; | 
					
						
							|  |  |  |  |   GList *events; | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  |   GFileOutputStream *output_stream; | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | } TestData; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  | output_event (const RecordedEvent *event) | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   if (event->step >= 0) | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |     g_test_message (">>>> step %d", event->step); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |   else | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       GTypeClass *class; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       class = g_type_class_ref (g_type_from_name ("GFileMonitorEvent")); | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |       g_test_message ("%s file=%s other_file=%s\n", | 
					
						
							|  |  |  |  |                       g_enum_get_value (G_ENUM_CLASS (class), event->event_type)->value_nick, | 
					
						
							|  |  |  |  |                       event->file, | 
					
						
							|  |  |  |  |                       event->other_file); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |       g_type_class_unref (class); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* a placeholder for temp file names we don't want to compare */ | 
					
						
							|  |  |  |  | static const gchar DONT_CARE[] = ""; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  | static Environment | 
					
						
							|  |  |  |  | get_environment (GFileMonitor *monitor) | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2024-07-24 16:02:30 +03:00
										 |  |  |  | #if defined(FILE_MONITOR_BACKEND_INOTIFY)
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |     return INOTIFY; | 
					
						
							| 
									
										
										
										
											2024-07-24 16:02:30 +03:00
										 |  |  |  | #elif defined(FILE_MONITOR_BACKEND_KQUEUE)
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |     return KQUEUE; | 
					
						
							| 
									
										
										
										
											2024-07-24 16:02:30 +03:00
										 |  |  |  | #elif defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
 | 
					
						
							|  |  |  |  |     return INOTIFY | KQUEUE; | 
					
						
							|  |  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   return NONE; | 
					
						
							| 
									
										
										
										
											2024-07-24 16:02:30 +03:00
										 |  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | check_expected_events (RecordedEvent *expected, | 
					
						
							|  |  |  |  |                        gsize          n_expected, | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |                        GList         *recorded, | 
					
						
							|  |  |  |  |                        Environment    env) | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-11-19 20:00:11 +01:00
										 |  |  |  |   gsize i; | 
					
						
							|  |  |  |  |   gint li; | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |   GList *l; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   for (i = 0, li = 0, l = recorded; i < n_expected && l != NULL;) | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |     { | 
					
						
							|  |  |  |  |       RecordedEvent *e1 = &expected[i]; | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |       RecordedEvent *e2 = l->data; | 
					
						
							|  |  |  |  |       gboolean mismatch = TRUE; | 
					
						
							|  |  |  |  |       gboolean l_extra_step = FALSE; | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |       do | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           gboolean ignore_other_file = FALSE; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           if (e1->step != e2->step) | 
					
						
							|  |  |  |  |             break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           /* Kqueue isn't good at detecting file renaming, so
 | 
					
						
							|  |  |  |  |            * G_FILE_MONITOR_WATCH_MOVES is mostly useless there.  */ | 
					
						
							|  |  |  |  |           if (e1->event_type != e2->event_type && env & KQUEUE) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |               /* It is possible for kqueue file monitor to emit 'RENAMED' event,
 | 
					
						
							|  |  |  |  |                * but most of the time it is reported as a 'DELETED' event and | 
					
						
							|  |  |  |  |                * a 'CREATED' event. */ | 
					
						
							|  |  |  |  |               if (e1->event_type == G_FILE_MONITOR_EVENT_RENAMED) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                   RecordedEvent *e2_next; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                   if (l->next == NULL) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  |                   e2_next = l->next->data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                   if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  |                   if (e2_next->event_type != G_FILE_MONITOR_EVENT_CREATED) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                   if (e1->step != e2_next->step) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                   if (e1->file != DONT_CARE && | 
					
						
							|  |  |  |  |                       (g_strcmp0 (e1->file, e2->file) != 0 || | 
					
						
							|  |  |  |  |                        e2->other_file != NULL)) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                   if (e1->other_file != DONT_CARE && | 
					
						
							|  |  |  |  |                       (g_strcmp0 (e1->other_file, e2_next->file) != 0 || | 
					
						
							|  |  |  |  |                        e2_next->other_file != NULL)) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |                   l_extra_step = TRUE; | 
					
						
							|  |  |  |  |                   mismatch = FALSE; | 
					
						
							|  |  |  |  |                   break; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |               /* Kqueue won't report 'MOVED_IN' and 'MOVED_OUT' events. We set
 | 
					
						
							|  |  |  |  |                * 'ignore_other_file' here to let the following code know that | 
					
						
							|  |  |  |  |                * 'other_file' may not match. */ | 
					
						
							|  |  |  |  |               else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_IN) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                   if (e2->event_type != G_FILE_MONITOR_EVENT_CREATED) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  |                   ignore_other_file = TRUE; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |               else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_OUT) | 
					
						
							|  |  |  |  |                 { | 
					
						
							|  |  |  |  |                   if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED) | 
					
						
							|  |  |  |  |                     break; | 
					
						
							|  |  |  |  |                   ignore_other_file = TRUE; | 
					
						
							|  |  |  |  |                 } | 
					
						
							|  |  |  |  |               else | 
					
						
							|  |  |  |  |                 break; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           if (e1->file != DONT_CARE && | 
					
						
							|  |  |  |  |               g_strcmp0 (e1->file, e2->file) != 0) | 
					
						
							|  |  |  |  |             break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           if (e1->other_file != DONT_CARE && !ignore_other_file && | 
					
						
							|  |  |  |  |               g_strcmp0 (e1->other_file, e2->other_file) != 0) | 
					
						
							|  |  |  |  |             break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           mismatch = FALSE; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       while (0); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       if (mismatch) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           /* Sometimes the emission of 'CHANGES_DONE_HINT' may be late because
 | 
					
						
							|  |  |  |  |            * it depends on the ability of file monitor implementation to report | 
					
						
							|  |  |  |  |            * 'CHANGES_DONE_HINT' itself. If the file monitor implementation | 
					
						
							|  |  |  |  |            * doesn't report 'CHANGES_DONE_HINT' itself, it may be emitted by | 
					
						
							|  |  |  |  |            * GLocalFileMonitor after a few seconds, which causes the event to | 
					
						
							|  |  |  |  |            * mix with results from different steps. Since 'CHANGES_DONE_HINT' | 
					
						
							|  |  |  |  |            * is just a hint, we don't require it to be reliable and we simply | 
					
						
							|  |  |  |  |            * ignore unexpected 'CHANGES_DONE_HINT' events here. */ | 
					
						
							|  |  |  |  |           if (e1->event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT && | 
					
						
							|  |  |  |  |               e2->event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |               g_test_message ("Event CHANGES_DONE_HINT ignored at " | 
					
						
							| 
									
										
										
										
											2020-11-19 20:00:11 +01:00
										 |  |  |  |                               "expected index %"  G_GSIZE_FORMAT ", recorded index %d", i, li); | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |               li++, l = l->next; | 
					
						
							|  |  |  |  |               continue; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |           /* If an event is marked as optional in the current environment and
 | 
					
						
							|  |  |  |  |            * the event doesn't match, it means the expected event has lost. */ | 
					
						
							|  |  |  |  |           else if (env & e1->optional) | 
					
						
							|  |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2020-11-19 20:00:11 +01:00
										 |  |  |  |               g_test_message ("Event %d at expected index %" G_GSIZE_FORMAT " skipped because " | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |                               "it is marked as optional", e1->event_type, i); | 
					
						
							|  |  |  |  |               i++; | 
					
						
							|  |  |  |  |               continue; | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |           /* Run above checks under g_assert_* again to provide more useful
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |            * error messages. Print the expected and actual events first. */ | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |           else | 
					
						
							|  |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2022-01-19 18:28:17 +01:00
										 |  |  |  |               GList *ll; | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |               gsize j; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |               g_test_message ("Recorded events:"); | 
					
						
							| 
									
										
										
										
											2022-01-19 18:28:17 +01:00
										 |  |  |  |               for (ll = recorded; ll != NULL; ll = ll->next) | 
					
						
							|  |  |  |  |                 output_event ((RecordedEvent *) ll->data); | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |               g_test_message ("Expected events:"); | 
					
						
							|  |  |  |  |               for (j = 0; j < n_expected; j++) | 
					
						
							|  |  |  |  |                 output_event (&expected[j]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |               g_assert_cmpint (e1->step, ==, e2->step); | 
					
						
							|  |  |  |  |               g_assert_cmpint (e1->event_type, ==, e2->event_type); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |               if (e1->file != DONT_CARE) | 
					
						
							|  |  |  |  |                 g_assert_cmpstr (e1->file, ==, e2->file); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |               if (e1->other_file != DONT_CARE) | 
					
						
							|  |  |  |  |                 g_assert_cmpstr (e1->other_file, ==, e2->other_file); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |               g_assert_not_reached (); | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       i++, li++, l = l->next; | 
					
						
							|  |  |  |  |       if (l_extra_step) | 
					
						
							|  |  |  |  |         li++, l = l->next; | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_assert_cmpint (i, ==, n_expected); | 
					
						
							|  |  |  |  |   g_assert_cmpint (li, ==, g_list_length (recorded)); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | record_event (TestData    *data, | 
					
						
							|  |  |  |  |               gint         event_type, | 
					
						
							|  |  |  |  |               const gchar *file, | 
					
						
							|  |  |  |  |               const gchar *other_file, | 
					
						
							|  |  |  |  |               gint         step) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   RecordedEvent *event; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   event = g_new0 (RecordedEvent, 1); | 
					
						
							|  |  |  |  |   event->event_type = event_type; | 
					
						
							|  |  |  |  |   event->file = g_strdup (file); | 
					
						
							|  |  |  |  |   event->other_file = g_strdup (other_file); | 
					
						
							|  |  |  |  |   event->step = step; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data->events = g_list_append (data->events, event); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							|  |  |  |  | monitor_changed (GFileMonitor      *monitor, | 
					
						
							|  |  |  |  |                  GFile             *file, | 
					
						
							|  |  |  |  |                  GFile             *other_file, | 
					
						
							|  |  |  |  |                  GFileMonitorEvent  event_type, | 
					
						
							|  |  |  |  |                  gpointer           user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   gchar *basename, *other_base; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   basename = g_file_get_basename (file); | 
					
						
							|  |  |  |  |   if (other_file) | 
					
						
							|  |  |  |  |     other_base = g_file_get_basename (other_file); | 
					
						
							|  |  |  |  |   else | 
					
						
							|  |  |  |  |     other_base = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   record_event (data, event_type, basename, other_base, -1); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_free (basename); | 
					
						
							|  |  |  |  |   g_free (other_base); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | atomic_replace_step (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (data->step) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case 0: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 0); | 
					
						
							|  |  |  |  |       g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 1: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 2: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 2); | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |       g_file_delete (data->file, NULL, NULL); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 3: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 3); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |       g_main_loop_quit (data->loop); | 
					
						
							|  |  |  |  |       return G_SOURCE_REMOVE; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data->step++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* this is the output we expect from the above steps */ | 
					
						
							|  |  |  |  | static RecordedEvent atomic_replace_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 0, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CREATED, "atomic_replace_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "atomic_replace_file", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "atomic_replace_file", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE, "atomic_replace_file", -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "atomic_replace_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE } | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | test_atomic_replace (Fixture       *fixture, | 
					
						
							|  |  |  |  |                      gconstpointer  user_data) | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   TestData data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  |   if (skip_win32 ()) | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |   data.step = 0; | 
					
						
							|  |  |  |  |   data.events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data.file = g_file_get_child (fixture->tmp_dir, "atomic_replace_file"); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |   g_file_delete (data.file, NULL, NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |   g_file_monitor_set_rate_limit (data.monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.loop = g_main_loop_new (NULL, TRUE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   g_timeout_add (500, atomic_replace_step, &data); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_main_loop_run (data.loop); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   check_expected_events (atomic_replace_output, | 
					
						
							|  |  |  |  |                          G_N_ELEMENTS (atomic_replace_output), | 
					
						
							|  |  |  |  |                          data.events, | 
					
						
							|  |  |  |  |                          get_environment (data.monitor)); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event); | 
					
						
							|  |  |  |  |   g_main_loop_unref (data.loop); | 
					
						
							|  |  |  |  |   g_object_unref (data.monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data.file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | change_step (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   GOutputStream *stream; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   guint32 mode = 0660; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (data->step) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case 0: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 0); | 
					
						
							|  |  |  |  |       g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 1: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       stream = (GOutputStream *)g_file_append_to (data->file, G_FILE_CREATE_NONE, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_output_stream_write_all (stream, " step 1", 7, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_output_stream_close (stream, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							| 
									
										
										
										
											2018-02-26 20:03:02 +00:00
										 |  |  |  |       g_object_unref (stream); | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 2: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 2); | 
					
						
							|  |  |  |  |       g_file_set_attribute (data->file, | 
					
						
							|  |  |  |  |                             G_FILE_ATTRIBUTE_UNIX_MODE, | 
					
						
							|  |  |  |  |                             G_FILE_ATTRIBUTE_TYPE_UINT32, | 
					
						
							|  |  |  |  |                             &mode, | 
					
						
							|  |  |  |  |                             G_FILE_QUERY_INFO_NONE, | 
					
						
							|  |  |  |  |                             NULL, | 
					
						
							|  |  |  |  |                             &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 3: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 3); | 
					
						
							|  |  |  |  |       g_file_delete (data->file, NULL, NULL); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 4: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 4); | 
					
						
							|  |  |  |  |       g_main_loop_quit (data->loop); | 
					
						
							|  |  |  |  |       return G_SOURCE_REMOVE; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data->step++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* this is the output we expect from the above steps */ | 
					
						
							|  |  |  |  | static RecordedEvent change_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 0, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CREATED, "change_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, "change_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "change_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 4, NONE } | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | test_file_changes (Fixture       *fixture, | 
					
						
							|  |  |  |  |                    gconstpointer  user_data) | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   TestData data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  |   if (skip_win32 ()) | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   data.step = 0; | 
					
						
							|  |  |  |  |   data.events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data.file = g_file_get_child (fixture->tmp_dir, "change_file"); | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   g_file_delete (data.file, NULL, NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   g_file_monitor_set_rate_limit (data.monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.loop = g_main_loop_new (NULL, TRUE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_timeout_add (500, change_step, &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_main_loop_run (data.loop); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   check_expected_events (change_output, | 
					
						
							|  |  |  |  |                          G_N_ELEMENTS (change_output), | 
					
						
							|  |  |  |  |                          data.events, | 
					
						
							|  |  |  |  |                          get_environment (data.monitor)); | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event); | 
					
						
							|  |  |  |  |   g_main_loop_unref (data.loop); | 
					
						
							|  |  |  |  |   g_object_unref (data.monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data.file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static gboolean | 
					
						
							|  |  |  |  | dir_step (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   GFile *parent, *file, *file2; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (data->step) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case 1: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       parent = g_file_get_parent (data->file); | 
					
						
							|  |  |  |  |       file = g_file_get_child (parent, "dir_test_file"); | 
					
						
							|  |  |  |  |       g_file_replace_contents (file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (file); | 
					
						
							|  |  |  |  |       g_object_unref (parent); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 2: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 2); | 
					
						
							|  |  |  |  |       parent = g_file_get_parent (data->file); | 
					
						
							|  |  |  |  |       file = g_file_get_child (parent, "dir_test_file"); | 
					
						
							|  |  |  |  |       file2 = g_file_get_child (data->file, "dir_test_file"); | 
					
						
							|  |  |  |  |       g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (file); | 
					
						
							|  |  |  |  |       g_object_unref (file2); | 
					
						
							|  |  |  |  |       g_object_unref (parent); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 3: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 3); | 
					
						
							|  |  |  |  |       file = g_file_get_child (data->file, "dir_test_file"); | 
					
						
							|  |  |  |  |       file2 = g_file_get_child (data->file, "dir_test_file2"); | 
					
						
							|  |  |  |  |       g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (file); | 
					
						
							|  |  |  |  |       g_object_unref (file2); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 4: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 4); | 
					
						
							|  |  |  |  |       parent = g_file_get_parent (data->file); | 
					
						
							|  |  |  |  |       file = g_file_get_child (data->file, "dir_test_file2"); | 
					
						
							|  |  |  |  |       file2 = g_file_get_child (parent, "dir_test_file2"); | 
					
						
							|  |  |  |  |       g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_file_delete (file2, NULL, NULL); | 
					
						
							|  |  |  |  |       g_object_unref (file); | 
					
						
							|  |  |  |  |       g_object_unref (file2); | 
					
						
							|  |  |  |  |       g_object_unref (parent); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 5: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 5); | 
					
						
							|  |  |  |  |       g_file_delete (data->file, NULL, NULL); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 6: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 6); | 
					
						
							|  |  |  |  |       g_main_loop_quit (data->loop); | 
					
						
							|  |  |  |  |       return G_SOURCE_REMOVE; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data->step++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | /* this is the output we expect from the above steps */ | 
					
						
							|  |  |  |  | static RecordedEvent dir_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_MOVED_IN, "dir_test_file", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_RENAMED, "dir_test_file", "dir_test_file2", -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 4, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_MOVED_OUT, "dir_test_file2", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 5, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "dir_monitor_test", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 6, NONE } | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | test_dir_monitor (Fixture       *fixture, | 
					
						
							|  |  |  |  |                   gconstpointer  user_data) | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   TestData data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  |   if (skip_win32 ()) | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   data.step = 0; | 
					
						
							|  |  |  |  |   data.events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data.file = g_file_get_child (fixture->tmp_dir, "dir_monitor_test"); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  |   g_file_delete (data.file, NULL, NULL); | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   g_file_make_directory (data.file, NULL, &error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.monitor = g_file_monitor_directory (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 19:27:58 -04:00
										 |  |  |  |   g_file_monitor_set_rate_limit (data.monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.loop = g_main_loop_new (NULL, TRUE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_timeout_add (500, dir_step, &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_main_loop_run (data.loop); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   check_expected_events (dir_output, | 
					
						
							|  |  |  |  |                          G_N_ELEMENTS (dir_output), | 
					
						
							|  |  |  |  |                          data.events, | 
					
						
							|  |  |  |  |                          get_environment (data.monitor)); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event); | 
					
						
							|  |  |  |  |   g_main_loop_unref (data.loop); | 
					
						
							|  |  |  |  |   g_object_unref (data.monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data.file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  | static gboolean | 
					
						
							|  |  |  |  | nodir_step (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   GFile *parent; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (data->step) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case 0: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 0); | 
					
						
							|  |  |  |  |       parent = g_file_get_parent (data->file); | 
					
						
							|  |  |  |  |       g_file_make_directory (parent, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (parent); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 1: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 2: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 2); | 
					
						
							|  |  |  |  |       g_file_delete (data->file, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 3: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 3); | 
					
						
							|  |  |  |  |       parent = g_file_get_parent (data->file); | 
					
						
							|  |  |  |  |       g_file_delete (parent, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (parent); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 4: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 4); | 
					
						
							|  |  |  |  |       g_main_loop_quit (data->loop); | 
					
						
							|  |  |  |  |       return G_SOURCE_REMOVE; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data->step++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static RecordedEvent nodir_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 0, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "nosuchfile", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "nosuchfile", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 4, NONE } | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | test_dir_non_existent (Fixture       *fixture, | 
					
						
							|  |  |  |  |                        gconstpointer  user_data) | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   TestData data; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  |   if (skip_win32 ()) | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  |   data.step = 0; | 
					
						
							|  |  |  |  |   data.events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data.file = g_file_get_child (fixture->tmp_dir, "nosuchdir/nosuchfile"); | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  |   data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  |   g_file_monitor_set_rate_limit (data.monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.loop = g_main_loop_new (NULL, TRUE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* we need a long timeout here, since the inotify implementation only scans
 | 
					
						
							|  |  |  |  |    * for missing files every 4 seconds. | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   g_timeout_add (5000, nodir_step, &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_main_loop_run (data.loop); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   check_expected_events (nodir_output, | 
					
						
							|  |  |  |  |                          G_N_ELEMENTS (nodir_output), | 
					
						
							|  |  |  |  |                          data.events, | 
					
						
							|  |  |  |  |                          get_environment (data.monitor)); | 
					
						
							| 
									
										
										
										
											2015-08-20 22:30:19 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event); | 
					
						
							|  |  |  |  |   g_main_loop_unref (data.loop); | 
					
						
							|  |  |  |  |   g_object_unref (data.monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data.file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  | static gboolean | 
					
						
							|  |  |  |  | cross_dir_step (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   GFile *file, *file2; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (data[0].step) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case 0: | 
					
						
							|  |  |  |  |       record_event (&data[0], -1, NULL, NULL, 0); | 
					
						
							|  |  |  |  |       record_event (&data[1], -1, NULL, NULL, 0); | 
					
						
							|  |  |  |  |       file = g_file_get_child (data[1].file, "a"); | 
					
						
							|  |  |  |  |       g_file_replace_contents (file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (file); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 1: | 
					
						
							|  |  |  |  |       record_event (&data[0], -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       record_event (&data[1], -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       file = g_file_get_child (data[1].file, "a"); | 
					
						
							|  |  |  |  |       file2 = g_file_get_child (data[0].file, "a"); | 
					
						
							|  |  |  |  |       g_file_move (file, file2, 0, NULL, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_object_unref (file); | 
					
						
							|  |  |  |  |       g_object_unref (file2); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 2: | 
					
						
							|  |  |  |  |       record_event (&data[0], -1, NULL, NULL, 2); | 
					
						
							|  |  |  |  |       record_event (&data[1], -1, NULL, NULL, 2); | 
					
						
							|  |  |  |  |       file2 = g_file_get_child (data[0].file, "a"); | 
					
						
							|  |  |  |  |       g_file_delete (file2, NULL, NULL); | 
					
						
							|  |  |  |  |       g_file_delete (data[0].file, NULL, NULL); | 
					
						
							|  |  |  |  |       g_file_delete (data[1].file, NULL, NULL); | 
					
						
							|  |  |  |  |       g_object_unref (file2); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 3: | 
					
						
							|  |  |  |  |       record_event (&data[0], -1, NULL, NULL, 3); | 
					
						
							|  |  |  |  |       record_event (&data[1], -1, NULL, NULL, 3); | 
					
						
							|  |  |  |  |       g_main_loop_quit (data->loop); | 
					
						
							|  |  |  |  |       return G_SOURCE_REMOVE; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data->step++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static RecordedEvent cross_dir_a_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 0, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "a", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_a", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE }, | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static RecordedEvent cross_dir_b_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 0, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "a", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_MOVED_OUT, "a", "a", -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_b", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE }, | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | test_cross_dir_moves (Fixture       *fixture, | 
					
						
							|  |  |  |  |                       gconstpointer  user_data) | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   TestData data[2]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  |   if (skip_win32 ()) | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  |   data[0].step = 0; | 
					
						
							|  |  |  |  |   data[0].events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data[0].file = g_file_get_child (fixture->tmp_dir, "cross_dir_a"); | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  |   g_file_delete (data[0].file, NULL, NULL); | 
					
						
							|  |  |  |  |   g_file_make_directory (data[0].file, NULL, &error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data[0].monitor = g_file_monitor_directory (data[0].file, 0, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor 0 %s", G_OBJECT_TYPE_NAME (data[0].monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  |   g_file_monitor_set_rate_limit (data[0].monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data[0].monitor, "changed", G_CALLBACK (monitor_changed), &data[0]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data[1].step = 0; | 
					
						
							|  |  |  |  |   data[1].events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data[1].file = g_file_get_child (fixture->tmp_dir, "cross_dir_b"); | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  |   g_file_delete (data[1].file, NULL, NULL); | 
					
						
							|  |  |  |  |   g_file_make_directory (data[1].file, NULL, &error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data[1].monitor = g_file_monitor_directory (data[1].file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor 1 %s", G_OBJECT_TYPE_NAME (data[1].monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  |   g_file_monitor_set_rate_limit (data[1].monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data[1].monitor, "changed", G_CALLBACK (monitor_changed), &data[1]); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data[0].loop = g_main_loop_new (NULL, TRUE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_timeout_add (500, cross_dir_step, data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_main_loop_run (data[0].loop); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   check_expected_events (cross_dir_a_output, | 
					
						
							|  |  |  |  |                          G_N_ELEMENTS (cross_dir_a_output), | 
					
						
							|  |  |  |  |                          data[0].events, | 
					
						
							|  |  |  |  |                          get_environment (data[0].monitor)); | 
					
						
							|  |  |  |  |   check_expected_events (cross_dir_b_output, | 
					
						
							|  |  |  |  |                          G_N_ELEMENTS (cross_dir_b_output), | 
					
						
							|  |  |  |  |                          data[1].events, | 
					
						
							|  |  |  |  |                          get_environment (data[1].monitor)); | 
					
						
							| 
									
										
										
										
											2015-08-21 00:41:09 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_list_free_full (data[0].events, (GDestroyNotify)free_recorded_event); | 
					
						
							|  |  |  |  |   g_main_loop_unref (data[0].loop); | 
					
						
							|  |  |  |  |   g_object_unref (data[0].monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data[0].file); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_list_free_full (data[1].events, (GDestroyNotify)free_recorded_event); | 
					
						
							|  |  |  |  |   g_object_unref (data[1].monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data[1].file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | static gboolean | 
					
						
							|  |  |  |  | file_hard_links_step (gpointer user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   gboolean retval = G_SOURCE_CONTINUE; | 
					
						
							|  |  |  |  |   TestData *data = user_data; | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   gchar *filename = g_file_get_path (data->file); | 
					
						
							|  |  |  |  |   gchar *hard_link_name = g_strdup_printf ("%s2", filename); | 
					
						
							|  |  |  |  |   GFile *hard_link_file = g_file_new_for_path (hard_link_name); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   switch (data->step) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |     case 0: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 0); | 
					
						
							|  |  |  |  |       g_output_stream_write_all (G_OUTPUT_STREAM (data->output_stream), | 
					
						
							|  |  |  |  |                                  "hello, step 0", 13, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       g_output_stream_close (G_OUTPUT_STREAM (data->output_stream), NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 1: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 1); | 
					
						
							|  |  |  |  |       g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, | 
					
						
							|  |  |  |  |                                G_FILE_CREATE_NONE, NULL, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 2: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 2); | 
					
						
							|  |  |  |  | #ifdef HAVE_LINK
 | 
					
						
							|  |  |  |  |       if (link (filename, hard_link_name) < 0) | 
					
						
							|  |  |  |  |         { | 
					
						
							|  |  |  |  |           g_error ("link(%s, %s) failed: %s", filename, hard_link_name, g_strerror (errno)); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | #endif  /* HAVE_LINK */
 | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 3: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 3); | 
					
						
							|  |  |  |  | #ifdef HAVE_LINK
 | 
					
						
							|  |  |  |  |       { | 
					
						
							|  |  |  |  |         GOutputStream *hard_link_stream = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         /* Deliberately don’t do an atomic swap on the hard-linked file. */ | 
					
						
							|  |  |  |  |         hard_link_stream = G_OUTPUT_STREAM (g_file_append_to (hard_link_file, | 
					
						
							|  |  |  |  |                                                               G_FILE_CREATE_NONE, | 
					
						
							|  |  |  |  |                                                               NULL, &error)); | 
					
						
							|  |  |  |  |         g_assert_no_error (error); | 
					
						
							|  |  |  |  |         g_output_stream_write_all (hard_link_stream, " step 3", 7, NULL, NULL, &error); | 
					
						
							|  |  |  |  |         g_assert_no_error (error); | 
					
						
							|  |  |  |  |         g_output_stream_close (hard_link_stream, NULL, &error); | 
					
						
							|  |  |  |  |         g_assert_no_error (error); | 
					
						
							|  |  |  |  |         g_object_unref (hard_link_stream); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | #endif  /* HAVE_LINK */
 | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 4: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 4); | 
					
						
							|  |  |  |  |       g_file_delete (data->file, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 5: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 5); | 
					
						
							|  |  |  |  | #ifdef HAVE_LINK
 | 
					
						
							|  |  |  |  |       g_file_delete (hard_link_file, NULL, &error); | 
					
						
							|  |  |  |  |       g_assert_no_error (error); | 
					
						
							|  |  |  |  | #endif  /* HAVE_LINK */
 | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     case 6: | 
					
						
							|  |  |  |  |       record_event (data, -1, NULL, NULL, 6); | 
					
						
							|  |  |  |  |       g_main_loop_quit (data->loop); | 
					
						
							|  |  |  |  |       retval = G_SOURCE_REMOVE; | 
					
						
							|  |  |  |  |       break; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   if (retval != G_SOURCE_REMOVE) | 
					
						
							|  |  |  |  |     data->step++; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_object_unref (hard_link_file); | 
					
						
							|  |  |  |  |   g_free (hard_link_name); | 
					
						
							|  |  |  |  |   g_free (filename); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   return retval; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static RecordedEvent file_hard_links_output[] = { | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |   { -1, NULL, NULL, 0, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "testfilemonitor.db", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 1, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE /* .goutputstream-XXXXXX */, "testfilemonitor.db", -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 2, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 3, NONE }, | 
					
						
							|  |  |  |  |   /* Kqueue is based on file descriptors. You can get events from all hard
 | 
					
						
							|  |  |  |  |    * links by just monitoring one open file descriptor, and it is not possible | 
					
						
							|  |  |  |  |    * to know whether it is done on the file name we use to open the file. Since | 
					
						
							|  |  |  |  |    * the hard link count of 'testfilemonitor.db' is 2, it is expected to see | 
					
						
							|  |  |  |  |    * two 'DELETED' events reported here. You have to call 'unlink' twice on | 
					
						
							|  |  |  |  |    * different file names to remove 'testfilemonitor.db' from the file system, | 
					
						
							|  |  |  |  |    * and each 'unlink' call generates a 'DELETED' event. */ | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, INOTIFY }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 4, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, NONE }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 5, NONE }, | 
					
						
							|  |  |  |  |   { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, INOTIFY }, | 
					
						
							|  |  |  |  |   { -1, NULL, NULL, 6, NONE }, | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | static void | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  | test_file_hard_links (Fixture       *fixture, | 
					
						
							|  |  |  |  |                       gconstpointer  user_data) | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | { | 
					
						
							|  |  |  |  |   GError *error = NULL; | 
					
						
							|  |  |  |  |   TestData data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-13 22:12:29 +01:00
										 |  |  |  |   g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755721"); | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-24 02:07:28 +04:00
										 |  |  |  |   if (skip_win32 ()) | 
					
						
							|  |  |  |  |     return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | #ifdef HAVE_LINK
 | 
					
						
							|  |  |  |  |   g_test_message ("Running with hard link tests"); | 
					
						
							|  |  |  |  | #else  /* if !HAVE_LINK */
 | 
					
						
							|  |  |  |  |   g_test_message ("Running without hard link tests"); | 
					
						
							|  |  |  |  | #endif  /* !HAVE_LINK */
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.step = 0; | 
					
						
							|  |  |  |  |   data.events = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Create a file which exists and is not a directory. */ | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   data.file = g_file_get_child (fixture->tmp_dir, "testfilemonitor.db"); | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  |   data.output_stream = g_file_replace (data.file, NULL, FALSE, | 
					
						
							|  |  |  |  |                                        G_FILE_CREATE_NONE, NULL, &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* Monitor it. Creating the monitor should not crash (bug #755721). */ | 
					
						
							|  |  |  |  |   data.monitor = g_file_monitor_file (data.file, | 
					
						
							|  |  |  |  |                                       G_FILE_MONITOR_WATCH_MOUNTS | | 
					
						
							|  |  |  |  |                                       G_FILE_MONITOR_WATCH_MOVES | | 
					
						
							|  |  |  |  |                                       G_FILE_MONITOR_WATCH_HARD_LINKS, | 
					
						
							|  |  |  |  |                                       NULL, | 
					
						
							|  |  |  |  |                                       &error); | 
					
						
							|  |  |  |  |   g_assert_no_error (error); | 
					
						
							|  |  |  |  |   g_assert_nonnull (data.monitor); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 14:58:45 +01:00
										 |  |  |  |   g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor)); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  |   /* Change the file a bit. */ | 
					
						
							|  |  |  |  |   g_file_monitor_set_rate_limit (data.monitor, 200); | 
					
						
							|  |  |  |  |   g_signal_connect (data.monitor, "changed", (GCallback) monitor_changed, &data); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   data.loop = g_main_loop_new (NULL, TRUE); | 
					
						
							|  |  |  |  |   g_timeout_add (500, file_hard_links_step, &data); | 
					
						
							|  |  |  |  |   g_main_loop_run (data.loop); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   check_expected_events (file_hard_links_output, | 
					
						
							| 
									
										
										
										
											2018-06-03 22:32:36 +08:00
										 |  |  |  |                          G_N_ELEMENTS (file_hard_links_output), | 
					
						
							|  |  |  |  |                          data.events, | 
					
						
							|  |  |  |  |                          get_environment (data.monitor)); | 
					
						
							| 
									
										
										
										
											2018-02-26 11:55:03 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_list_free_full (data.events, (GDestroyNotify) free_recorded_event); | 
					
						
							|  |  |  |  |   g_main_loop_unref (data.loop); | 
					
						
							|  |  |  |  |   g_object_unref (data.monitor); | 
					
						
							|  |  |  |  |   g_object_unref (data.file); | 
					
						
							|  |  |  |  |   g_object_unref (data.output_stream); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-30 17:55:43 +01:00
										 |  |  |  | static void | 
					
						
							|  |  |  |  | test_finalize_in_callback (Fixture       *fixture, | 
					
						
							|  |  |  |  |                            gconstpointer  user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFile *file = NULL; | 
					
						
							|  |  |  |  |   guint i; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_test_summary ("Test that finalization of a GFileMonitor in one of its " | 
					
						
							|  |  |  |  |                   "callbacks doesn’t cause a deadlock."); | 
					
						
							|  |  |  |  |   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1941"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   file = g_file_get_child (fixture->tmp_dir, "race-file"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   for (i = 0; i < 50; i++) | 
					
						
							|  |  |  |  |     { | 
					
						
							|  |  |  |  |       GFileMonitor *monitor = NULL; | 
					
						
							|  |  |  |  |       GError *local_error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Monitor the file. */ | 
					
						
							|  |  |  |  |       monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error); | 
					
						
							|  |  |  |  |       g_assert_no_error (local_error); | 
					
						
							|  |  |  |  |       g_assert_nonnull (monitor); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Create the file. */ | 
					
						
							|  |  |  |  |       g_file_replace_contents (file, "hello", 5, NULL, FALSE, | 
					
						
							|  |  |  |  |                                G_FILE_CREATE_NONE, NULL, NULL, &local_error); | 
					
						
							|  |  |  |  |       g_assert_no_error (local_error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Immediately drop the last ref to the monitor in the hope that this
 | 
					
						
							|  |  |  |  |        * happens in the middle of the critical section in | 
					
						
							|  |  |  |  |        * g_file_monitor_source_handle_event(), so that any cleanup at the end | 
					
						
							|  |  |  |  |        * of that function is done with a now-finalised file monitor. */ | 
					
						
							|  |  |  |  |       g_object_unref (monitor); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Re-create the monitor and do the same again for deleting the file, to
 | 
					
						
							|  |  |  |  |        * give a second chance at hitting the race condition. */ | 
					
						
							|  |  |  |  |       monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error); | 
					
						
							|  |  |  |  |       g_assert_no_error (local_error); | 
					
						
							|  |  |  |  |       g_assert_nonnull (monitor); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Delete the file. */ | 
					
						
							|  |  |  |  |       g_file_delete (file, NULL, &local_error); | 
					
						
							|  |  |  |  |       g_assert_no_error (local_error); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       /* Drop the ref again. */ | 
					
						
							|  |  |  |  |       g_object_unref (monitor); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_object_unref (file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-15 14:45:10 +00:00
										 |  |  |  | static void | 
					
						
							|  |  |  |  | test_root (Fixture       *fixture, | 
					
						
							|  |  |  |  |            gconstpointer  user_data) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   GFile *file = NULL; | 
					
						
							|  |  |  |  |   GFileMonitor *monitor = NULL; | 
					
						
							|  |  |  |  |   GError *local_error = NULL; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_test_summary ("Test that GFileMonitor can monitor the root directory."); | 
					
						
							|  |  |  |  |   g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3241"); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | #if defined(G_OS_UNIX)
 | 
					
						
							|  |  |  |  |   file = g_file_new_for_path ("/"); | 
					
						
							|  |  |  |  | #elif defined(G_OS_WIN32)
 | 
					
						
							|  |  |  |  |   file = g_file_new_for_path ("C:\\"); | 
					
						
							|  |  |  |  | #else
 | 
					
						
							|  |  |  |  |   g_test_skip ("Unsupported root directory"); | 
					
						
							|  |  |  |  |   return; | 
					
						
							|  |  |  |  | #endif
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /* We can’t test for any monitor events, but we can at least check that this
 | 
					
						
							|  |  |  |  |    * doesn’t crash or error. */ | 
					
						
							|  |  |  |  |   monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &local_error); | 
					
						
							|  |  |  |  |   g_assert_no_error (local_error); | 
					
						
							|  |  |  |  |   g_assert_nonnull (monitor); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   g_clear_object (&monitor); | 
					
						
							|  |  |  |  |   g_clear_object (&file); | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | int | 
					
						
							|  |  |  |  | main (int argc, char *argv[]) | 
					
						
							|  |  |  |  | { | 
					
						
							|  |  |  |  |   g_test_init (&argc, &argv, NULL); | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-10 15:19:34 +01:00
										 |  |  |  |   g_test_add ("/monitor/atomic-replace", Fixture, NULL, setup, test_atomic_replace, teardown); | 
					
						
							|  |  |  |  |   g_test_add ("/monitor/file-changes", Fixture, NULL, setup, test_file_changes, teardown); | 
					
						
							|  |  |  |  |   g_test_add ("/monitor/dir-monitor", Fixture, NULL, setup, test_dir_monitor, teardown); | 
					
						
							|  |  |  |  |   g_test_add ("/monitor/dir-not-existent", Fixture, NULL, setup, test_dir_non_existent, teardown); | 
					
						
							|  |  |  |  |   g_test_add ("/monitor/cross-dir-moves", Fixture, NULL, setup, test_cross_dir_moves, teardown); | 
					
						
							|  |  |  |  |   g_test_add ("/monitor/file/hard-links", Fixture, NULL, setup, test_file_hard_links, teardown); | 
					
						
							| 
									
										
										
										
											2022-05-30 17:55:43 +01:00
										 |  |  |  |   g_test_add ("/monitor/finalize-in-callback", Fixture, NULL, setup, test_finalize_in_callback, teardown); | 
					
						
							| 
									
										
										
										
											2023-03-15 14:45:10 +00:00
										 |  |  |  |   g_test_add ("/monitor/root", Fixture, NULL, setup, test_root, teardown); | 
					
						
							| 
									
										
										
										
											2015-08-19 07:10:55 -04:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |   return g_test_run (); | 
					
						
							|  |  |  |  | } |