[glib: 2/4] tests: Make testfilemonitor test work with kqueue



commit 454a9f8de9087acd441f99d72cb58a0595b8c3da
Author: Ting-Wei Lan <lantw src gnome org>
Date:   Sun Jun 3 22:32:36 2018 +0800

    tests: Make testfilemonitor test work with kqueue
    
    check_expected_events is heavily modified in this commit to tolerate
    event loss and allow renaming to be reported as creation and deletion.
    
    This fixes test failure on FreeBSD.

 gio/tests/testfilemonitor.c | 359 +++++++++++++++++++++++++++++++-------------
 1 file changed, 258 insertions(+), 101 deletions(-)
---
diff --git a/gio/tests/testfilemonitor.c b/gio/tests/testfilemonitor.c
index 6ef5d38f5..0fa72d3d9 100644
--- a/gio/tests/testfilemonitor.c
+++ b/gio/tests/testfilemonitor.c
@@ -9,12 +9,25 @@
  * the tests, e.g. the length of timeouts
  */
 
+typedef enum {
+  NONE      = 0,
+  INOTIFY   = (1 << 1),
+  KQUEUE    = (1 << 2)
+} Environment;
+
 typedef struct
 {
   gint event_type;
   gchar *file;
   gchar *other_file;
   gint step;
+
+  /* Since different file monitor implementation has different capabilities,
+   * we cannot expect all implementations to report all kind of events without
+   * any loss. This 'optional' field is a bit mask used to mark events which
+   * may be lost under specific platforms.
+   */
+  Environment optional;
 } RecordedEvent;
 
 static void
@@ -68,41 +81,158 @@ output_events (GList *list)
 /* a placeholder for temp file names we don't want to compare */
 static const gchar DONT_CARE[] = "";
 
-static void
-check_expected_event (gint           i,
-                      RecordedEvent *e1,
-                      RecordedEvent *e2)
+static Environment
+get_environment (GFileMonitor *monitor)
 {
-  g_assert_cmpint (e1->step, ==, e2->step);
-  if (e1->step < 0)
-    return;
-
-  g_assert_cmpint (e1->event_type, ==, e2->event_type);
-
-  if (e1->file != DONT_CARE)
-    g_assert_cmpstr (e1->file, ==, e2->file);
-
-  if (e1->other_file != DONT_CARE)
-    g_assert_cmpstr (e1->other_file, ==, e2->other_file);
+  if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GInotifyFileMonitor"))
+    return INOTIFY;
+  if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GKqueueFileMonitor"))
+    return KQUEUE;
+  return NONE;
 }
 
 static void
 check_expected_events (RecordedEvent *expected,
                        gsize          n_expected,
-                       GList         *recorded)
+                       GList         *recorded,
+                       Environment    env)
 {
-  gint i;
+  gint i, li;
   GList *l;
 
-  g_assert_cmpint (n_expected, ==, g_list_length (recorded));
-
-  for (i = 0, l = recorded; i < n_expected; i++, l = l->next)
+  for (i = 0, li = 0, l = recorded; i < n_expected && l != NULL;)
     {
       RecordedEvent *e1 = &expected[i];
-      RecordedEvent *e2 = (RecordedEvent *)l->data;
+      RecordedEvent *e2 = l->data;
+      gboolean mismatch = TRUE;
+      gboolean l_extra_step = FALSE;
 
-      check_expected_event (i, e1, e2);
+      do
+        {
+          gboolean ignore_other_file = FALSE;
+
+          if (e1->step != e2->step)
+            break;
+
+          /* Kqueue isn't good at detecting file renaming, so
+           * G_FILE_MONITOR_WATCH_MOVES is mostly useless there.  */
+          if (e1->event_type != e2->event_type && env & KQUEUE)
+            {
+              /* It is possible for kqueue file monitor to emit 'RENAMED' event,
+               * but most of the time it is reported as a 'DELETED' event and
+               * a 'CREATED' event. */
+              if (e1->event_type == G_FILE_MONITOR_EVENT_RENAMED)
+                {
+                  RecordedEvent *e2_next;
+
+                  if (l->next == NULL)
+                    break;
+                  e2_next = l->next->data;
+
+                  if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
+                    break;
+                  if (e2_next->event_type != G_FILE_MONITOR_EVENT_CREATED)
+                    break;
+
+                  if (e1->step != e2_next->step)
+                    break;
+
+                  if (e1->file != DONT_CARE &&
+                      (g_strcmp0 (e1->file, e2->file) != 0 ||
+                       e2->other_file != NULL))
+                    break;
+
+                  if (e1->other_file != DONT_CARE &&
+                      (g_strcmp0 (e1->other_file, e2_next->file) != 0 ||
+                       e2_next->other_file != NULL))
+                    break;
+
+                  l_extra_step = TRUE;
+                  mismatch = FALSE;
+                  break;
+                }
+              /* Kqueue won't report 'MOVED_IN' and 'MOVED_OUT' events. We set
+               * 'ignore_other_file' here to let the following code know that
+               * 'other_file' may not match. */
+              else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_IN)
+                {
+                  if (e2->event_type != G_FILE_MONITOR_EVENT_CREATED)
+                    break;
+                  ignore_other_file = TRUE;
+                }
+              else if (e1->event_type == G_FILE_MONITOR_EVENT_MOVED_OUT)
+                {
+                  if (e2->event_type != G_FILE_MONITOR_EVENT_DELETED)
+                    break;
+                  ignore_other_file = TRUE;
+                }
+              else
+                break;
+            }
+
+          if (e1->file != DONT_CARE &&
+              g_strcmp0 (e1->file, e2->file) != 0)
+            break;
+
+          if (e1->other_file != DONT_CARE && !ignore_other_file &&
+              g_strcmp0 (e1->other_file, e2->other_file) != 0)
+            break;
+
+          mismatch = FALSE;
+        }
+      while (0);
+
+      if (mismatch)
+        {
+          /* Sometimes the emission of 'CHANGES_DONE_HINT' may be late because
+           * it depends on the ability of file monitor implementation to report
+           * 'CHANGES_DONE_HINT' itself. If the file monitor implementation
+           * doesn't report 'CHANGES_DONE_HINT' itself, it may be emitted by
+           * GLocalFileMonitor after a few seconds, which causes the event to
+           * mix with results from different steps. Since 'CHANGES_DONE_HINT'
+           * is just a hint, we don't require it to be reliable and we simply
+           * ignore unexpected 'CHANGES_DONE_HINT' events here. */
+          if (e1->event_type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT &&
+              e2->event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+            {
+              g_test_message ("Event CHANGES_DONE_HINT ignored at "
+                              "expected index %d, recorded index %d", i, li);
+              li++, l = l->next;
+              continue;
+            }
+          /* If an event is marked as optional in the current environment and
+           * the event doesn't match, it means the expected event has lost. */
+          else if (env & e1->optional)
+            {
+              g_test_message ("Event %d at expected index %d skipped because "
+                              "it is marked as optional", e1->event_type, i);
+              i++;
+              continue;
+            }
+          /* Run above checks under g_assert_* again to provide more useful
+           * error messages. */
+          else
+            {
+              g_assert_cmpint (e1->step, ==, e2->step);
+              g_assert_cmpint (e1->event_type, ==, e2->event_type);
+
+              if (e1->file != DONT_CARE)
+                g_assert_cmpstr (e1->file, ==, e2->file);
+
+              if (e1->other_file != DONT_CARE)
+                g_assert_cmpstr (e1->other_file, ==, e2->other_file);
+
+              g_assert_not_reached ();
+            }
+        }
+
+      i++, li++, l = l->next;
+      if (l_extra_step)
+        li++, l = l->next;
     }
+
+  g_assert_cmpint (i, ==, n_expected);
+  g_assert_cmpint (li, ==, g_list_length (recorded));
 }
 
 static void
@@ -180,15 +310,15 @@ atomic_replace_step (gpointer user_data)
 
 /* this is the output we expect from the above steps */
 static RecordedEvent atomic_replace_output[] = {
-  { -1, NULL, NULL, 0 },
-  { G_FILE_MONITOR_EVENT_CREATED, "atomic_replace_file", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGED, "atomic_replace_file", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "atomic_replace_file", NULL, -1 },
-  { -1, NULL, NULL, 1 },
-  { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE, "atomic_replace_file", -1 },
-  { -1, NULL, NULL, 2 },
-  { G_FILE_MONITOR_EVENT_DELETED, "atomic_replace_file", NULL, -1 },
-  { -1, NULL, NULL, 3 }
+  { -1, NULL, NULL, 0, NONE },
+  { G_FILE_MONITOR_EVENT_CREATED, "atomic_replace_file", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGED, "atomic_replace_file", NULL, -1, KQUEUE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "atomic_replace_file", NULL, -1, KQUEUE },
+  { -1, NULL, NULL, 1, NONE },
+  { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE, "atomic_replace_file", -1, NONE },
+  { -1, NULL, NULL, 2, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "atomic_replace_file", NULL, -1, NONE },
+  { -1, NULL, NULL, 3, NONE }
 };
 
 static void
@@ -216,7 +346,10 @@ test_atomic_replace (void)
   g_main_loop_run (data.loop);
 
   /*output_events (data.events);*/
-  check_expected_events (atomic_replace_output, G_N_ELEMENTS (atomic_replace_output), data.events);
+  check_expected_events (atomic_replace_output,
+                         G_N_ELEMENTS (atomic_replace_output),
+                         data.events,
+                         get_environment (data.monitor));
 
   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
   g_main_loop_unref (data.loop);
@@ -277,18 +410,18 @@ change_step (gpointer user_data)
 
 /* this is the output we expect from the above steps */
 static RecordedEvent change_output[] = {
-  { -1, NULL, NULL, 0 },
-  { G_FILE_MONITOR_EVENT_CREATED, "change_file", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1 },
-  { -1, NULL, NULL, 1 },
-  { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1 },
-  { -1, NULL, NULL, 2 },
-  { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, "change_file", NULL, -1 },
-  { -1, NULL, NULL, 3 },
-  { G_FILE_MONITOR_EVENT_DELETED, "change_file", NULL, -1 },
-  { -1, NULL, NULL, 4 }
+  { -1, NULL, NULL, 0, NONE },
+  { G_FILE_MONITOR_EVENT_CREATED, "change_file", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, KQUEUE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, KQUEUE },
+  { -1, NULL, NULL, 1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGED, "change_file", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "change_file", NULL, -1, NONE },
+  { -1, NULL, NULL, 2, NONE },
+  { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, "change_file", NULL, -1, NONE },
+  { -1, NULL, NULL, 3, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "change_file", NULL, -1, NONE },
+  { -1, NULL, NULL, 4, NONE }
 };
 
 static void
@@ -316,7 +449,10 @@ test_file_changes (void)
   g_main_loop_run (data.loop);
 
   /*output_events (data.events);*/
-  check_expected_events (change_output, G_N_ELEMENTS (change_output), data.events);
+  check_expected_events (change_output,
+                         G_N_ELEMENTS (change_output),
+                         data.events,
+                         get_environment (data.monitor));
 
   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
   g_main_loop_unref (data.loop);
@@ -391,16 +527,16 @@ dir_step (gpointer user_data)
 
 /* this is the output we expect from the above steps */
 static RecordedEvent dir_output[] = {
-  { -1, NULL, NULL, 1 },
-  { -1, NULL, NULL, 2 },
-  { G_FILE_MONITOR_EVENT_MOVED_IN, "dir_test_file", NULL, -1 },
-  { -1, NULL, NULL, 3 },
-  { G_FILE_MONITOR_EVENT_RENAMED, "dir_test_file", "dir_test_file2", -1 },
-  { -1, NULL, NULL, 4 },
-  { G_FILE_MONITOR_EVENT_MOVED_OUT, "dir_test_file2", NULL, -1 },
-  { -1, NULL, NULL, 5 },
-  { G_FILE_MONITOR_EVENT_DELETED, "dir_monitor_test", NULL, -1 },
-  { -1, NULL, NULL, 6 }
+  { -1, NULL, NULL, 1, NONE },
+  { -1, NULL, NULL, 2, NONE },
+  { G_FILE_MONITOR_EVENT_MOVED_IN, "dir_test_file", NULL, -1, NONE },
+  { -1, NULL, NULL, 3, NONE },
+  { G_FILE_MONITOR_EVENT_RENAMED, "dir_test_file", "dir_test_file2", -1, NONE },
+  { -1, NULL, NULL, 4, NONE },
+  { G_FILE_MONITOR_EVENT_MOVED_OUT, "dir_test_file2", NULL, -1, NONE },
+  { -1, NULL, NULL, 5, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "dir_monitor_test", NULL, -1, NONE },
+  { -1, NULL, NULL, 6, NONE }
 };
 
 static void
@@ -429,7 +565,10 @@ test_dir_monitor (void)
   g_main_loop_run (data.loop);
 
   /*output_events (data.events);*/
-  check_expected_events (dir_output, G_N_ELEMENTS (dir_output), data.events);
+  check_expected_events (dir_output,
+                         G_N_ELEMENTS (dir_output),
+                         data.events,
+                         get_environment (data.monitor));
 
   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
   g_main_loop_unref (data.loop);
@@ -482,17 +621,17 @@ nodir_step (gpointer user_data)
 }
 
 static RecordedEvent nodir_output[] = {
-  { -1, NULL, NULL, 0 },
-  { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1 },
-  { -1, NULL, NULL, 1 },
-  { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGED, "nosuchfile", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1 },
-  { -1, NULL, NULL, 2 },
-  { G_FILE_MONITOR_EVENT_DELETED, "nosuchfile", NULL, -1 },
-  { -1, NULL, NULL, 3 },
-  { -1, NULL, NULL, 4 }
+  { -1, NULL, NULL, 0, NONE },
+  { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, KQUEUE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
+  { -1, NULL, NULL, 1, NONE },
+  { G_FILE_MONITOR_EVENT_CREATED, "nosuchfile", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGED, "nosuchfile", NULL, -1, KQUEUE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "nosuchfile", NULL, -1, KQUEUE },
+  { -1, NULL, NULL, 2, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "nosuchfile", NULL, -1, NONE },
+  { -1, NULL, NULL, 3, NONE },
+  { -1, NULL, NULL, 4, NONE }
 };
 
 static void
@@ -521,7 +660,10 @@ test_dir_non_existent (void)
   g_main_loop_run (data.loop);
 
   /*output_events (data.events);*/
-  check_expected_events (nodir_output, G_N_ELEMENTS (nodir_output), data.events);
+  check_expected_events (nodir_output,
+                         G_N_ELEMENTS (nodir_output),
+                         data.events,
+                         get_environment (data.monitor));
 
   g_list_free_full (data.events, (GDestroyNotify)free_recorded_event);
   g_main_loop_unref (data.loop);
@@ -578,26 +720,26 @@ cross_dir_step (gpointer user_data)
 }
 
 static RecordedEvent cross_dir_a_output[] = {
-  { -1, NULL, NULL, 0 },
-  { -1, NULL, NULL, 1 },
-  { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1 },
-  { -1, NULL, NULL, 2 },
-  { G_FILE_MONITOR_EVENT_DELETED, "a", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_a", NULL, -1 },
-  { -1, NULL, NULL, 3 },
+  { -1, NULL, NULL, 0, NONE },
+  { -1, NULL, NULL, 1, NONE },
+  { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
+  { -1, NULL, NULL, 2, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "a", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_a", NULL, -1, NONE },
+  { -1, NULL, NULL, 3, NONE },
 };
 
 static RecordedEvent cross_dir_b_output[] = {
-  { -1, NULL, NULL, 0 },
-  { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGED, "a", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1 },
-  { -1, NULL, NULL, 1 },
-  { G_FILE_MONITOR_EVENT_MOVED_OUT, "a", "a", -1 },
-  { -1, NULL, NULL, 2 },
-  { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_b", NULL, -1 },
-  { -1, NULL, NULL, 3 },
+  { -1, NULL, NULL, 0, NONE },
+  { G_FILE_MONITOR_EVENT_CREATED, "a", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGED, "a", NULL, -1, KQUEUE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "a", NULL, -1, KQUEUE },
+  { -1, NULL, NULL, 1, NONE },
+  { G_FILE_MONITOR_EVENT_MOVED_OUT, "a", "a", -1, NONE },
+  { -1, NULL, NULL, 2, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "cross_dir_b", NULL, -1, NONE },
+  { -1, NULL, NULL, 3, NONE },
 };
 static void
 test_cross_dir_moves (void)
@@ -644,8 +786,14 @@ test_cross_dir_moves (void)
   output_events (data[1].events);
 #endif
 
-  check_expected_events (cross_dir_a_output, G_N_ELEMENTS (cross_dir_a_output), data[0].events);
-  check_expected_events (cross_dir_b_output, G_N_ELEMENTS (cross_dir_b_output), data[1].events);
+  check_expected_events (cross_dir_a_output,
+                         G_N_ELEMENTS (cross_dir_a_output),
+                         data[0].events,
+                         get_environment (data[0].monitor));
+  check_expected_events (cross_dir_b_output,
+                         G_N_ELEMENTS (cross_dir_b_output),
+                         data[1].events,
+                         get_environment (data[1].monitor));
 
   g_list_free_full (data[0].events, (GDestroyNotify)free_recorded_event);
   g_main_loop_unref (data[0].loop);
@@ -742,19 +890,26 @@ file_hard_links_step (gpointer user_data)
 }
 
 static RecordedEvent file_hard_links_output[] = {
-  { -1, NULL, NULL, 0 },
-  { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1 },
-  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "testfilemonitor.db", NULL, -1 },
-  { -1, NULL, NULL, 1 },
-  { G_FILE_MONITOR_EVENT_RENAMED, NULL  /* .goutputstream-XXXXXX */, "testfilemonitor.db", -1 },
-  { -1, NULL, NULL, 2 },
-  { -1, NULL, NULL, 3 },
-  /* FIXME: There should be a EVENT_CHANGED and EVENT_CHANGES_DONE_HINT here
-   * from the modification of the hard link. */
-  { -1, NULL, NULL, 4 },
-  { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1 },
-  { -1, NULL, NULL, 5 },
-  { -1, NULL, NULL, 6 },
+  { -1, NULL, NULL, 0, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, NONE },
+  { G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, "testfilemonitor.db", NULL, -1, NONE },
+  { -1, NULL, NULL, 1, NONE },
+  { G_FILE_MONITOR_EVENT_RENAMED, (gchar*)DONT_CARE /* .goutputstream-XXXXXX */, "testfilemonitor.db", -1, 
NONE },
+  { -1, NULL, NULL, 2, NONE },
+  { -1, NULL, NULL, 3, NONE },
+  /* Kqueue is based on file descriptors. You can get events from all hard
+   * links by just monitoring one open file descriptor, and it is not possible
+   * to know whether it is done on the file name we use to open the file. Since
+   * the hard link count of 'testfilemonitor.db' is 2, it is expected to see
+   * two 'DELETED' events reported here. You have to call 'unlink' twice on
+   * different file names to remove 'testfilemonitor.db' from the file system,
+   * and each 'unlink' call generates a 'DELETED' event. */
+  { G_FILE_MONITOR_EVENT_CHANGED, "testfilemonitor.db", NULL, -1, INOTIFY },
+  { -1, NULL, NULL, 4, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, NONE },
+  { -1, NULL, NULL, 5, NONE },
+  { G_FILE_MONITOR_EVENT_DELETED, "testfilemonitor.db", NULL, -1, INOTIFY },
+  { -1, NULL, NULL, 6, NONE },
 };
 
 static void
@@ -800,7 +955,9 @@ test_file_hard_links (void)
 
   /* output_events (data.events); */
   check_expected_events (file_hard_links_output,
-                         G_N_ELEMENTS (file_hard_links_output), data.events);
+                         G_N_ELEMENTS (file_hard_links_output),
+                         data.events,
+                         get_environment (data.monitor));
 
   g_list_free_full (data.events, (GDestroyNotify) free_recorded_event);
   g_main_loop_unref (data.loop);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]