(root)/
glib-2.79.0/
gio/
tests/
testfilemonitor.c
       1  #include "config.h"
       2  
       3  #include <errno.h>
       4  #include <stdlib.h>
       5  #include <gio/gio.h>
       6  
       7  static gboolean
       8  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    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  setup (Fixture       *fixture,
      30         gconstpointer  user_data)
      31  {
      32    gchar *path = NULL;
      33    GError *local_error = NULL;
      34  
      35    path = g_dir_make_tmp ("gio-test-testfilemonitor_XXXXXX", &local_error);
      36    g_assert_no_error (local_error);
      37  
      38    fixture->tmp_dir = g_file_new_for_path (path);
      39  
      40    g_test_message ("Using temporary directory: %s", path);
      41  
      42    g_free (path);
      43  }
      44  
      45  static void
      46  teardown (Fixture       *fixture,
      47            gconstpointer  user_data)
      48  {
      49    GError *local_error = NULL;
      50  
      51    g_file_delete (fixture->tmp_dir, NULL, &local_error);
      52    g_assert_no_error (local_error);
      53    g_clear_object (&fixture->tmp_dir);
      54  }
      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  free_recorded_event (RecordedEvent *event)
      79  {
      80    g_free (event->file);
      81    g_free (event->other_file);
      82    g_free (event);
      83  }
      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  output_event (const RecordedEvent *event)
      97  {
      98    if (event->step >= 0)
      99      g_test_message (">>>> step %d", event->step);
     100    else
     101      {
     102        GTypeClass *class;
     103  
     104        class = g_type_class_ref (g_type_from_name ("GFileMonitorEvent"));
     105        g_test_message ("%s file=%s other_file=%s\n",
     106                        g_enum_get_value (G_ENUM_CLASS (class), event->event_type)->value_nick,
     107                        event->file,
     108                        event->other_file);
     109        g_type_class_unref (class);
     110      }
     111  }
     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  get_environment (GFileMonitor *monitor)
     118  {
     119    if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GInotifyFileMonitor"))
     120      return INOTIFY;
     121    if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GKqueueFileMonitor"))
     122      return KQUEUE;
     123    return NONE;
     124  }
     125  
     126  static void
     127  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    for (i = 0, li = 0, l = recorded; i < n_expected && l != NULL;)
     137      {
     138        RecordedEvent *e1 = &expected[i];
     139        RecordedEvent *e2 = l->data;
     140        gboolean mismatch = TRUE;
     141        gboolean l_extra_step = FALSE;
     142  
     143        do
     144          {
     145            gboolean ignore_other_file = FALSE;
     146  
     147            if (e1->step != e2->step)
     148              break;
     149  
     150            /* Kqueue isn't good at detecting file renaming, so
     151             * G_FILE_MONITOR_WATCH_MOVES is mostly useless there.  */
     152            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                if (e1->event_type == G_FILE_MONITOR_EVENT_RENAMED)
     158                  {
     159                    RecordedEvent *e2_next;
     160  
     161                    if (l->next == NULL)
     162                      break;
     163                    e2_next = l->next->data;
     164  
     165                    if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
     166                      break;
     167                    if (e2_next->event_type != G_FILE_MONITOR_EVENT_CREATED)
     168                      break;
     169  
     170                    if (e1->step != e2_next->step)
     171                      break;
     172  
     173                    if (e1->file != DONT_CARE &&
     174                        (g_strcmp0 (e1->file, e2->file) != 0 ||
     175                         e2->other_file != NULL))
     176                      break;
     177  
     178                    if (e1->other_file != DONT_CARE &&
     179                        (g_strcmp0 (e1->other_file, e2_next->file) != 0 ||
     180                         e2_next->other_file != NULL))
     181                      break;
     182  
     183                    l_extra_step = TRUE;
     184                    mismatch = FALSE;
     185                    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                else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_IN)
     191                  {
     192                    if (e2->event_type != G_FILE_MONITOR_EVENT_CREATED)
     193                      break;
     194                    ignore_other_file = TRUE;
     195                  }
     196                else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_OUT)
     197                  {
     198                    if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
     199                      break;
     200                    ignore_other_file = TRUE;
     201                  }
     202                else
     203                  break;
     204              }
     205  
     206            if (e1->file != DONT_CARE &&
     207                g_strcmp0 (e1->file, e2->file) != 0)
     208              break;
     209  
     210            if (e1->other_file != DONT_CARE && !ignore_other_file &&
     211                g_strcmp0 (e1->other_file, e2->other_file) != 0)
     212              break;
     213  
     214            mismatch = FALSE;
     215          }
     216        while (0);
     217  
     218        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            if (e1->event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
     229                e2->event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
     230              {
     231                g_test_message ("Event CHANGES_DONE_HINT ignored at "
     232                                "expected index %"  G_GSIZE_FORMAT ", recorded index %d", i, li);
     233                li++, l = l->next;
     234                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            else if (env & e1->optional)
     239              {
     240                g_test_message ("Event %d at expected index %" G_GSIZE_FORMAT " skipped because "
     241                                "it is marked as optional", e1->event_type, i);
     242                i++;
     243                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                g_test_message ("Recorded events:");
     253                for (ll = recorded; ll != NULL; ll = ll->next)
     254                  output_event ((RecordedEvent *) ll->data);
     255  
     256                g_test_message ("Expected events:");
     257                for (j = 0; j < n_expected; j++)
     258                  output_event (&expected[j]);
     259  
     260                g_assert_cmpint (e1->step, ==, e2->step);
     261                g_assert_cmpint (e1->event_type, ==, e2->event_type);
     262  
     263                if (e1->file != DONT_CARE)
     264                  g_assert_cmpstr (e1->file, ==, e2->file);
     265  
     266                if (e1->other_file != DONT_CARE)
     267                  g_assert_cmpstr (e1->other_file, ==, e2->other_file);
     268  
     269                g_assert_not_reached ();
     270              }
     271          }
     272  
     273        i++, li++, l = l->next;
     274        if (l_extra_step)
     275          li++, l = l->next;
     276      }
     277  
     278    g_assert_cmpint (i, ==, n_expected);
     279    g_assert_cmpint (li, ==, g_list_length (recorded));
     280  }
     281  
     282  static void
     283  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    event = g_new0 (RecordedEvent, 1);
     292    event->event_type = event_type;
     293    event->file = g_strdup (file);
     294    event->other_file = g_strdup (other_file);
     295    event->step = step;
     296  
     297    data->events = g_list_append (data->events, event);
     298  }
     299  
     300  static void
     301  monitor_changed (GFileMonitor      *monitor,
     302                   GFile             *file,
     303                   GFile             *other_file,
     304                   GFileMonitorEvent  event_type,
     305                   gpointer           user_data)
     306  {
     307    TestData *data = user_data;
     308    gchar *basename, *other_base;
     309  
     310    basename = g_file_get_basename (file);
     311    if (other_file)
     312      other_base = g_file_get_basename (other_file);
     313    else
     314      other_base = NULL;
     315  
     316    record_event (data, event_type, basename, other_base, -1);
     317  
     318    g_free (basename);
     319    g_free (other_base);
     320  }
     321  
     322  static gboolean
     323  atomic_replace_step (gpointer user_data)
     324  {
     325    TestData *data = user_data;
     326    GError *error = NULL;
     327  
     328    switch (data->step)
     329      {
     330      case 0:
     331        record_event (data, -1, NULL, NULL, 0);
     332        g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
     333        g_assert_no_error (error);
     334        break;
     335      case 1:
     336        record_event (data, -1, NULL, NULL, 1);
     337        g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
     338        g_assert_no_error (error);
     339        break;
     340      case 2:
     341        record_event (data, -1, NULL, NULL, 2);
     342        g_file_delete (data->file, NULL, NULL);
     343        break;
     344      case 3:
     345        record_event (data, -1, NULL, NULL, 3);
     346        g_main_loop_quit (data->loop);
     347        return G_SOURCE_REMOVE;
     348      }
     349  
     350    data->step++;
     351  
     352    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  test_atomic_replace (Fixture       *fixture,
     370                       gconstpointer  user_data)
     371  {
     372    GError *error = NULL;
     373    TestData data;
     374  
     375    if (skip_win32 ())
     376      return;
     377  
     378    data.step = 0;
     379    data.events = NULL;
     380  
     381    data.file = g_file_get_child (fixture->tmp_dir, "atomic_replace_file");
     382    g_file_delete (data.file, NULL, NULL);
     383  
     384    data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
     385    g_assert_no_error (error);
     386  
     387    g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
     388  
     389    g_file_monitor_set_rate_limit (data.monitor, 200);
     390    g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
     391  
     392    data.loop = g_main_loop_new (NULL, TRUE);
     393  
     394    g_timeout_add (500, atomic_replace_step, &data);
     395  
     396    g_main_loop_run (data.loop);
     397  
     398    check_expected_events (atomic_replace_output,
     399                           G_N_ELEMENTS (atomic_replace_output),
     400                           data.events,
     401                           get_environment (data.monitor));
     402  
     403    g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
     404    g_main_loop_unref (data.loop);
     405    g_object_unref (data.monitor);
     406    g_object_unref (data.file);
     407  }
     408  
     409  static gboolean
     410  change_step (gpointer user_data)
     411  {
     412    TestData *data = user_data;
     413    GOutputStream *stream;
     414    GError *error = NULL;
     415    guint32 mode = 0660;
     416  
     417    switch (data->step)
     418      {
     419      case 0:
     420        record_event (data, -1, NULL, NULL, 0);
     421        g_file_replace_contents (data->file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
     422        g_assert_no_error (error);
     423        break;
     424      case 1:
     425        record_event (data, -1, NULL, NULL, 1);
     426        stream = (GOutputStream *)g_file_append_to (data->file, G_FILE_CREATE_NONE, NULL, &error);
     427        g_assert_no_error (error);
     428        g_output_stream_write_all (stream, " step 1", 7, NULL, NULL, &error);
     429        g_assert_no_error (error);
     430        g_output_stream_close (stream, NULL, &error);
     431        g_assert_no_error (error);
     432        g_object_unref (stream);
     433        break;
     434      case 2:
     435        record_event (data, -1, NULL, NULL, 2);
     436        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        g_assert_no_error (error);
     444        break;
     445      case 3:
     446        record_event (data, -1, NULL, NULL, 3);
     447        g_file_delete (data->file, NULL, NULL);
     448        break;
     449      case 4:
     450        record_event (data, -1, NULL, NULL, 4);
     451        g_main_loop_quit (data->loop);
     452        return G_SOURCE_REMOVE;
     453      }
     454  
     455    data->step++;
     456  
     457    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  test_file_changes (Fixture       *fixture,
     478                     gconstpointer  user_data)
     479  {
     480    GError *error = NULL;
     481    TestData data;
     482  
     483    if (skip_win32 ())
     484      return;
     485  
     486    data.step = 0;
     487    data.events = NULL;
     488  
     489    data.file = g_file_get_child (fixture->tmp_dir, "change_file");
     490    g_file_delete (data.file, NULL, NULL);
     491  
     492    data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
     493    g_assert_no_error (error);
     494  
     495    g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
     496  
     497    g_file_monitor_set_rate_limit (data.monitor, 200);
     498    g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
     499  
     500    data.loop = g_main_loop_new (NULL, TRUE);
     501  
     502    g_timeout_add (500, change_step, &data);
     503  
     504    g_main_loop_run (data.loop);
     505  
     506    check_expected_events (change_output,
     507                           G_N_ELEMENTS (change_output),
     508                           data.events,
     509                           get_environment (data.monitor));
     510  
     511    g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
     512    g_main_loop_unref (data.loop);
     513    g_object_unref (data.monitor);
     514    g_object_unref (data.file);
     515  }
     516  
     517  static gboolean
     518  dir_step (gpointer user_data)
     519  {
     520    TestData *data = user_data;
     521    GFile *parent, *file, *file2;
     522    GError *error = NULL;
     523  
     524    switch (data->step)
     525      {
     526      case 1:
     527        record_event (data, -1, NULL, NULL, 1);
     528        parent = g_file_get_parent (data->file);
     529        file = g_file_get_child (parent, "dir_test_file");
     530        g_file_replace_contents (file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
     531        g_assert_no_error (error);
     532        g_object_unref (file);
     533        g_object_unref (parent);
     534        break;
     535      case 2:
     536        record_event (data, -1, NULL, NULL, 2);
     537        parent = g_file_get_parent (data->file);
     538        file = g_file_get_child (parent, "dir_test_file");
     539        file2 = g_file_get_child (data->file, "dir_test_file");
     540        g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
     541        g_assert_no_error (error);
     542        g_object_unref (file);
     543        g_object_unref (file2);
     544        g_object_unref (parent);
     545        break;
     546      case 3:
     547        record_event (data, -1, NULL, NULL, 3);
     548        file = g_file_get_child (data->file, "dir_test_file");
     549        file2 = g_file_get_child (data->file, "dir_test_file2");
     550        g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
     551        g_assert_no_error (error);
     552        g_object_unref (file);
     553        g_object_unref (file2);
     554        break;
     555      case 4:
     556        record_event (data, -1, NULL, NULL, 4);
     557        parent = g_file_get_parent (data->file);
     558        file = g_file_get_child (data->file, "dir_test_file2");
     559        file2 = g_file_get_child (parent, "dir_test_file2");
     560        g_file_move (file, file2, G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
     561        g_assert_no_error (error);
     562        g_file_delete (file2, NULL, NULL);
     563        g_object_unref (file);
     564        g_object_unref (file2);
     565        g_object_unref (parent);
     566        break;
     567      case 5:
     568        record_event (data, -1, NULL, NULL, 5);
     569        g_file_delete (data->file, NULL, NULL);
     570        break;
     571      case 6:
     572        record_event (data, -1, NULL, NULL, 6);
     573        g_main_loop_quit (data->loop);
     574        return G_SOURCE_REMOVE;
     575      }
     576  
     577    data->step++;
     578  
     579    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  test_dir_monitor (Fixture       *fixture,
     598                    gconstpointer  user_data)
     599  {
     600    GError *error = NULL;
     601    TestData data;
     602  
     603    if (skip_win32 ())
     604      return;
     605  
     606    data.step = 0;
     607    data.events = NULL;
     608  
     609    data.file = g_file_get_child (fixture->tmp_dir, "dir_monitor_test");
     610    g_file_delete (data.file, NULL, NULL);
     611    g_file_make_directory (data.file, NULL, &error);
     612  
     613    data.monitor = g_file_monitor_directory (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
     614    g_assert_no_error (error);
     615  
     616    g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
     617  
     618    g_file_monitor_set_rate_limit (data.monitor, 200);
     619    g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
     620  
     621    data.loop = g_main_loop_new (NULL, TRUE);
     622  
     623    g_timeout_add (500, dir_step, &data);
     624  
     625    g_main_loop_run (data.loop);
     626  
     627    check_expected_events (dir_output,
     628                           G_N_ELEMENTS (dir_output),
     629                           data.events,
     630                           get_environment (data.monitor));
     631  
     632    g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
     633    g_main_loop_unref (data.loop);
     634    g_object_unref (data.monitor);
     635    g_object_unref (data.file);
     636  }
     637  
     638  static gboolean
     639  nodir_step (gpointer user_data)
     640  {
     641    TestData *data = user_data;
     642    GFile *parent;
     643    GError *error = NULL;
     644  
     645    switch (data->step)
     646      {
     647      case 0:
     648        record_event (data, -1, NULL, NULL, 0);
     649        parent = g_file_get_parent (data->file);
     650        g_file_make_directory (parent, NULL, &error);
     651        g_assert_no_error (error);
     652        g_object_unref (parent);
     653        break;
     654      case 1:
     655        record_event (data, -1, NULL, NULL, 1);
     656        g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
     657        g_assert_no_error (error);
     658        break;
     659      case 2:
     660        record_event (data, -1, NULL, NULL, 2);
     661        g_file_delete (data->file, NULL, &error);
     662        g_assert_no_error (error);
     663        break;
     664      case 3:
     665        record_event (data, -1, NULL, NULL, 3);
     666        parent = g_file_get_parent (data->file);
     667        g_file_delete (parent, NULL, &error);
     668        g_assert_no_error (error);
     669        g_object_unref (parent);
     670        break;
     671      case 4:
     672        record_event (data, -1, NULL, NULL, 4);
     673        g_main_loop_quit (data->loop);
     674        return G_SOURCE_REMOVE;
     675      }
     676  
     677    data->step++;
     678  
     679    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  test_dir_non_existent (Fixture       *fixture,
     698                         gconstpointer  user_data)
     699  {
     700    TestData data;
     701    GError *error = NULL;
     702  
     703    if (skip_win32 ())
     704      return;
     705  
     706    data.step = 0;
     707    data.events = NULL;
     708  
     709    data.file = g_file_get_child (fixture->tmp_dir, "nosuchdir/nosuchfile");
     710    data.monitor = g_file_monitor_file (data.file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
     711    g_assert_no_error (error);
     712  
     713    g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
     714  
     715    g_file_monitor_set_rate_limit (data.monitor, 200);
     716    g_signal_connect (data.monitor, "changed", G_CALLBACK (monitor_changed), &data);
     717  
     718    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    g_timeout_add (5000, nodir_step, &data);
     724  
     725    g_main_loop_run (data.loop);
     726  
     727    check_expected_events (nodir_output,
     728                           G_N_ELEMENTS (nodir_output),
     729                           data.events,
     730                           get_environment (data.monitor));
     731  
     732    g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
     733    g_main_loop_unref (data.loop);
     734    g_object_unref (data.monitor);
     735    g_object_unref (data.file);
     736  }
     737  
     738  static gboolean
     739  cross_dir_step (gpointer user_data)
     740  {
     741    TestData *data = user_data;
     742    GFile *file, *file2;
     743    GError *error = NULL;
     744  
     745    switch (data[0].step)
     746      {
     747      case 0:
     748        record_event (&data[0], -1, NULL, NULL, 0);
     749        record_event (&data[1], -1, NULL, NULL, 0);
     750        file = g_file_get_child (data[1].file, "a");
     751        g_file_replace_contents (file, "step 0", 6, NULL, FALSE, G_FILE_CREATE_NONE, NULL, NULL, &error);
     752        g_assert_no_error (error);
     753        g_object_unref (file);
     754        break;
     755      case 1:
     756        record_event (&data[0], -1, NULL, NULL, 1);
     757        record_event (&data[1], -1, NULL, NULL, 1);
     758        file = g_file_get_child (data[1].file, "a");
     759        file2 = g_file_get_child (data[0].file, "a");
     760        g_file_move (file, file2, 0, NULL, NULL, NULL, &error);
     761        g_assert_no_error (error);
     762        g_object_unref (file);
     763        g_object_unref (file2);
     764        break;
     765      case 2:
     766        record_event (&data[0], -1, NULL, NULL, 2);
     767        record_event (&data[1], -1, NULL, NULL, 2);
     768        file2 = g_file_get_child (data[0].file, "a");
     769        g_file_delete (file2, NULL, NULL);
     770        g_file_delete (data[0].file, NULL, NULL);
     771        g_file_delete (data[1].file, NULL, NULL);
     772        g_object_unref (file2);
     773        break;
     774      case 3:
     775        record_event (&data[0], -1, NULL, NULL, 3);
     776        record_event (&data[1], -1, NULL, NULL, 3);
     777        g_main_loop_quit (data->loop);
     778        return G_SOURCE_REMOVE;
     779      }
     780  
     781    data->step++;
     782  
     783    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  test_cross_dir_moves (Fixture       *fixture,
     810                        gconstpointer  user_data)
     811  {
     812    GError *error = NULL;
     813    TestData data[2];
     814  
     815    if (skip_win32 ())
     816      return;
     817  
     818    data[0].step = 0;
     819    data[0].events = NULL;
     820  
     821    data[0].file = g_file_get_child (fixture->tmp_dir, "cross_dir_a");
     822    g_file_delete (data[0].file, NULL, NULL);
     823    g_file_make_directory (data[0].file, NULL, &error);
     824  
     825    data[0].monitor = g_file_monitor_directory (data[0].file, 0, NULL, &error);
     826    g_assert_no_error (error);
     827  
     828    g_test_message ("Using GFileMonitor 0 %s", G_OBJECT_TYPE_NAME (data[0].monitor));
     829  
     830    g_file_monitor_set_rate_limit (data[0].monitor, 200);
     831    g_signal_connect (data[0].monitor, "changed", G_CALLBACK (monitor_changed), &data[0]);
     832  
     833    data[1].step = 0;
     834    data[1].events = NULL;
     835  
     836    data[1].file = g_file_get_child (fixture->tmp_dir, "cross_dir_b");
     837    g_file_delete (data[1].file, NULL, NULL);
     838    g_file_make_directory (data[1].file, NULL, &error);
     839  
     840    data[1].monitor = g_file_monitor_directory (data[1].file, G_FILE_MONITOR_WATCH_MOVES, NULL, &error);
     841    g_assert_no_error (error);
     842  
     843    g_test_message ("Using GFileMonitor 1 %s", G_OBJECT_TYPE_NAME (data[1].monitor));
     844  
     845    g_file_monitor_set_rate_limit (data[1].monitor, 200);
     846    g_signal_connect (data[1].monitor, "changed", G_CALLBACK (monitor_changed), &data[1]);
     847  
     848    data[0].loop = g_main_loop_new (NULL, TRUE);
     849  
     850    g_timeout_add (500, cross_dir_step, data);
     851  
     852    g_main_loop_run (data[0].loop);
     853  
     854    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    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    g_list_free_full (data[0].events, (GDestroyNotify)free_recorded_event);
     864    g_main_loop_unref (data[0].loop);
     865    g_object_unref (data[0].monitor);
     866    g_object_unref (data[0].file);
     867  
     868    g_list_free_full (data[1].events, (GDestroyNotify)free_recorded_event);
     869    g_object_unref (data[1].monitor);
     870    g_object_unref (data[1].file);
     871  }
     872  
     873  static gboolean
     874  file_hard_links_step (gpointer user_data)
     875  {
     876    gboolean retval = G_SOURCE_CONTINUE;
     877    TestData *data = user_data;
     878    GError *error = NULL;
     879  
     880    gchar *filename = g_file_get_path (data->file);
     881    gchar *hard_link_name = g_strdup_printf ("%s2", filename);
     882    GFile *hard_link_file = g_file_new_for_path (hard_link_name);
     883  
     884    switch (data->step)
     885      {
     886      case 0:
     887        record_event (data, -1, NULL, NULL, 0);
     888        g_output_stream_write_all (G_OUTPUT_STREAM (data->output_stream),
     889                                   "hello, step 0", 13, NULL, NULL, &error);
     890        g_assert_no_error (error);
     891        g_output_stream_close (G_OUTPUT_STREAM (data->output_stream), NULL, &error);
     892        g_assert_no_error (error);
     893        break;
     894      case 1:
     895        record_event (data, -1, NULL, NULL, 1);
     896        g_file_replace_contents (data->file, "step 1", 6, NULL, FALSE,
     897                                 G_FILE_CREATE_NONE, NULL, NULL, &error);
     898        g_assert_no_error (error);
     899        break;
     900      case 2:
     901        record_event (data, -1, NULL, NULL, 2);
     902  #ifdef HAVE_LINK
     903        if (link (filename, hard_link_name) < 0)
     904          {
     905            g_error ("link(%s, %s) failed: %s", filename, hard_link_name, g_strerror (errno));
     906          }
     907  #endif  /* HAVE_LINK */
     908        break;
     909      case 3:
     910        record_event (data, -1, NULL, NULL, 3);
     911  #ifdef HAVE_LINK
     912        {
     913          GOutputStream *hard_link_stream = NULL;
     914  
     915          /* Deliberately don’t do an atomic swap on the hard-linked file. */
     916          hard_link_stream = G_OUTPUT_STREAM (g_file_append_to (hard_link_file,
     917                                                                G_FILE_CREATE_NONE,
     918                                                                NULL, &error));
     919          g_assert_no_error (error);
     920          g_output_stream_write_all (hard_link_stream, " step 3", 7, NULL, NULL, &error);
     921          g_assert_no_error (error);
     922          g_output_stream_close (hard_link_stream, NULL, &error);
     923          g_assert_no_error (error);
     924          g_object_unref (hard_link_stream);
     925        }
     926  #endif  /* HAVE_LINK */
     927        break;
     928      case 4:
     929        record_event (data, -1, NULL, NULL, 4);
     930        g_file_delete (data->file, NULL, &error);
     931        g_assert_no_error (error);
     932        break;
     933      case 5:
     934        record_event (data, -1, NULL, NULL, 5);
     935  #ifdef HAVE_LINK
     936        g_file_delete (hard_link_file, NULL, &error);
     937        g_assert_no_error (error);
     938  #endif  /* HAVE_LINK */
     939        break;
     940      case 6:
     941        record_event (data, -1, NULL, NULL, 6);
     942        g_main_loop_quit (data->loop);
     943        retval = G_SOURCE_REMOVE;
     944        break;
     945      }
     946  
     947    if (retval != G_SOURCE_REMOVE)
     948      data->step++;
     949  
     950    g_object_unref (hard_link_file);
     951    g_free (hard_link_name);
     952    g_free (filename);
     953  
     954    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  test_file_hard_links (Fixture       *fixture,
     982                        gconstpointer  user_data)
     983  {
     984    GError *error = NULL;
     985    TestData data;
     986  
     987    g_test_bug ("https://bugzilla.gnome.org/show_bug.cgi?id=755721");
     988  
     989    if (skip_win32 ())
     990      return;
     991  
     992  #ifdef HAVE_LINK
     993    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    data.step = 0;
     999    data.events = NULL;
    1000  
    1001    /* Create a file which exists and is not a directory. */
    1002    data.file = g_file_get_child (fixture->tmp_dir, "testfilemonitor.db");
    1003    data.output_stream = g_file_replace (data.file, NULL, FALSE,
    1004                                         G_FILE_CREATE_NONE, NULL, &error);
    1005    g_assert_no_error (error);
    1006  
    1007    /* Monitor it. Creating the monitor should not crash (bug #755721). */
    1008    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    g_assert_no_error (error);
    1015    g_assert_nonnull (data.monitor);
    1016  
    1017    g_test_message ("Using GFileMonitor %s", G_OBJECT_TYPE_NAME (data.monitor));
    1018  
    1019    /* Change the file a bit. */
    1020    g_file_monitor_set_rate_limit (data.monitor, 200);
    1021    g_signal_connect (data.monitor, "changed", (GCallback) monitor_changed, &data);
    1022  
    1023    data.loop = g_main_loop_new (NULL, TRUE);
    1024    g_timeout_add (500, file_hard_links_step, &data);
    1025    g_main_loop_run (data.loop);
    1026  
    1027    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    g_list_free_full (data.events, (GDestroyNotify) free_recorded_event);
    1033    g_main_loop_unref (data.loop);
    1034    g_object_unref (data.monitor);
    1035    g_object_unref (data.file);
    1036    g_object_unref (data.output_stream);
    1037  }
    1038  
    1039  static void
    1040  test_finalize_in_callback (Fixture       *fixture,
    1041                             gconstpointer  user_data)
    1042  {
    1043    GFile *file = NULL;
    1044    guint i;
    1045  
    1046    g_test_summary ("Test that finalization of a GFileMonitor in one of its "
    1047                    "callbacks doesn’t cause a deadlock.");
    1048    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/1941");
    1049  
    1050    file = g_file_get_child (fixture->tmp_dir, "race-file");
    1051  
    1052    for (i = 0; i < 50; i++)
    1053      {
    1054        GFileMonitor *monitor = NULL;
    1055        GError *local_error = NULL;
    1056  
    1057        /* Monitor the file. */
    1058        monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
    1059        g_assert_no_error (local_error);
    1060        g_assert_nonnull (monitor);
    1061  
    1062        /* Create the file. */
    1063        g_file_replace_contents (file, "hello", 5, NULL, FALSE,
    1064                                 G_FILE_CREATE_NONE, NULL, NULL, &local_error);
    1065        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        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        monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &local_error);
    1076        g_assert_no_error (local_error);
    1077        g_assert_nonnull (monitor);
    1078  
    1079        /* Delete the file. */
    1080        g_file_delete (file, NULL, &local_error);
    1081        g_assert_no_error (local_error);
    1082  
    1083        /* Drop the ref again. */
    1084        g_object_unref (monitor);
    1085      }
    1086  
    1087    g_object_unref (file);
    1088  }
    1089  
    1090  static void
    1091  test_root (Fixture       *fixture,
    1092             gconstpointer  user_data)
    1093  {
    1094    GFile *file = NULL;
    1095    GFileMonitor *monitor = NULL;
    1096    GError *local_error = NULL;
    1097  
    1098    g_test_summary ("Test that GFileMonitor can monitor the root directory.");
    1099    g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3241");
    1100  
    1101  #if defined(G_OS_UNIX)
    1102    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    monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, &local_error);
    1113    g_assert_no_error (local_error);
    1114    g_assert_nonnull (monitor);
    1115  
    1116    g_clear_object (&monitor);
    1117    g_clear_object (&file);
    1118  }
    1119  
    1120  int
    1121  main (int argc, char *argv[])
    1122  {
    1123    g_test_init (&argc, &argv, NULL);
    1124  
    1125    g_test_add ("/monitor/atomic-replace", Fixture, NULL, setup, test_atomic_replace, teardown);
    1126    g_test_add ("/monitor/file-changes", Fixture, NULL, setup, test_file_changes, teardown);
    1127    g_test_add ("/monitor/dir-monitor", Fixture, NULL, setup, test_dir_monitor, teardown);
    1128    g_test_add ("/monitor/dir-not-existent", Fixture, NULL, setup, test_dir_non_existent, teardown);
    1129    g_test_add ("/monitor/cross-dir-moves", Fixture, NULL, setup, test_cross_dir_moves, teardown);
    1130    g_test_add ("/monitor/file/hard-links", Fixture, NULL, setup, test_file_hard_links, teardown);
    1131    g_test_add ("/monitor/finalize-in-callback", Fixture, NULL, setup, test_finalize_in_callback, teardown);
    1132    g_test_add ("/monitor/root", Fixture, NULL, setup, test_root, teardown);
    1133  
    1134    return g_test_run ();
    1135  }