LCOV - code coverage report
Current view: top level - glib/gio/tests - testfilemonitor.c (source / functions) Hit Total Coverage
Test: unnamed Lines: 498 564 88.3 %
Date: 2024-04-23 05:16:05 Functions: 23 24 95.8 %
Branches: 64 131 48.9 %

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

Generated by: LCOV version 1.14