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

Generated by: LCOV version 2.0-1