LCOV - code coverage report
Current view: top level - gio/tests - testfilemonitor.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 88.1 % 565 498
Test Date: 2026-01-20 05:15:58 Functions: 95.8 % 24 23
Branches: - 0 0

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

Generated by: LCOV version 2.0-1