1  /* GLib testing framework examples and tests
       2   *
       3   * Copyright © 2013 Red Hat, Inc.
       4   * Copyright © 2015, 2017, 2018 Endless Mobile, Inc.
       5   *
       6   * SPDX-License-Identifier: LGPL-2.1-or-later
       7   *
       8   * This library is free software; you can redistribute it and/or
       9   * modify it under the terms of the GNU Lesser General Public
      10   * License as published by the Free Software Foundation; either
      11   * version 2.1 of the License, or (at your option) any later version.
      12   *
      13   * This library is distributed in the hope that it will be useful,
      14   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16   * Lesser General Public License for more details.
      17   *
      18   * You should have received a copy of the GNU Lesser General
      19   * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      20   */
      21  
      22  #include <gio/gio.h>
      23  #include <gstdio.h>
      24  
      25  #if defined (G_OS_UNIX) && !defined (__APPLE__)
      26  #include <gio/gdesktopappinfo.h>
      27  #endif
      28  
      29  typedef struct
      30  {
      31    gchar *applications_dir;
      32  } Fixture;
      33  
      34  static void
      35  setup (Fixture       *fixture,
      36         gconstpointer  user_data)
      37  {
      38    fixture->applications_dir = g_build_filename (g_get_user_data_dir (), "applications", NULL);
      39    g_assert_no_errno (g_mkdir_with_parents (fixture->applications_dir, 0755));
      40  
      41    g_test_message ("Using data directory: %s", g_get_user_data_dir ());
      42  }
      43  
      44  static void
      45  teardown (Fixture       *fixture,
      46            gconstpointer  user_data)
      47  {
      48    g_assert_no_errno (g_rmdir (fixture->applications_dir));
      49    g_clear_pointer (&fixture->applications_dir, g_free);
      50  }
      51  
      52  #if defined (G_OS_UNIX) && !defined (__APPLE__)
      53  static gboolean
      54  create_app (gpointer data)
      55  {
      56    const gchar *path = data;
      57    GError *error = NULL;
      58    const gchar *contents = 
      59      "[Desktop Entry]\n"
      60      "Name=Application\n"
      61      "Version=1.0\n"
      62      "Type=Application\n"
      63      "Exec=true\n";
      64  
      65    g_file_set_contents (path, contents, -1, &error);
      66    g_assert_no_error (error);
      67  
      68    return G_SOURCE_REMOVE;
      69  }
      70  
      71  static void
      72  delete_app (gpointer data)
      73  {
      74    const gchar *path = data;
      75  
      76    g_remove (path);
      77  }
      78  
      79  static void
      80  changed_cb (GAppInfoMonitor *monitor,
      81              gpointer         user_data)
      82  {
      83    gboolean *changed_fired = user_data;
      84  
      85    *changed_fired = TRUE;
      86    g_main_context_wakeup (g_main_context_get_thread_default ());
      87  }
      88  
      89  static gboolean
      90  timeout_cb (gpointer user_data)
      91  {
      92    gboolean *timed_out = user_data;
      93  
      94    g_assert (!timed_out);
      95    *timed_out = TRUE;
      96    g_main_context_wakeup (g_main_context_get_thread_default ());
      97  
      98    return G_SOURCE_REMOVE;
      99  }
     100  #endif  /* defined (G_OS_UNIX) && !defined (__APPLE__) */
     101  
     102  static void
     103  test_app_monitor (Fixture       *fixture,
     104                    gconstpointer  user_data)
     105  {
     106  #if defined (G_OS_UNIX) && !defined (__APPLE__)
     107    gchar *app_path;
     108    GAppInfoMonitor *monitor;
     109    GMainContext *context = NULL;  /* use the global default main context */
     110    GSource *timeout_source = NULL;
     111    GDesktopAppInfo *app = NULL;
     112    gboolean changed_fired = FALSE;
     113    gboolean timed_out = FALSE;
     114  
     115    app_path = g_build_filename (fixture->applications_dir, "app.desktop", NULL);
     116  
     117    /* FIXME: this shouldn't be required */
     118    g_list_free_full (g_app_info_get_all (), g_object_unref);
     119  
     120    /* Create an app monitor and check that its ::changed signal is emitted when
     121     * a new app is installed. */
     122    monitor = g_app_info_monitor_get ();
     123  
     124    g_signal_connect (monitor, "changed", G_CALLBACK (changed_cb), &changed_fired);
     125  
     126    g_idle_add (create_app, app_path);
     127    timeout_source = g_timeout_source_new_seconds (3);
     128    g_source_set_callback (timeout_source, timeout_cb, &timed_out, NULL);
     129    g_source_attach (timeout_source, NULL);
     130  
     131    while (!changed_fired && !timed_out)
     132      g_main_context_iteration (context, TRUE);
     133  
     134    g_assert_true (changed_fired);
     135    changed_fired = FALSE;
     136  
     137    g_source_destroy (timeout_source);
     138    g_clear_pointer (&timeout_source, g_source_unref);
     139  
     140    /* Check that the app is now queryable. This has the side-effect of re-arming
     141     * the #GAppInfoMonitor::changed signal for the next part of the test. */
     142    app = g_desktop_app_info_new ("app.desktop");
     143    g_assert_nonnull (app);
     144    g_clear_object (&app);
     145  
     146    /* Now check that ::changed is emitted when an app is uninstalled. */
     147    timeout_source = g_timeout_source_new_seconds (3);
     148    g_source_set_callback (timeout_source, timeout_cb, &timed_out, NULL);
     149    g_source_attach (timeout_source, NULL);
     150  
     151    delete_app (app_path);
     152  
     153    while (!changed_fired && !timed_out)
     154      g_main_context_iteration (context, TRUE);
     155  
     156    g_assert_true (changed_fired);
     157  
     158    g_source_destroy (timeout_source);
     159    g_clear_pointer (&timeout_source, g_source_unref);
     160    g_remove (app_path);
     161  
     162    g_object_unref (monitor);
     163  
     164    g_free (app_path);
     165  #elif defined (__APPLE__)
     166    g_test_skip (".desktop monitor on macos");
     167  #else  /* if !(defined (G_OS_UNIX) && !defined (__APPLE__)) */
     168    g_test_skip (".desktop monitor on win32");
     169  #endif  /* !(defined (G_OS_UNIX) && !defined (__APPLE__)) */
     170  }
     171  
     172  int
     173  main (int argc, char *argv[])
     174  {
     175    g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
     176  
     177    g_test_add ("/monitor/app", Fixture, NULL, setup, test_app_monitor, teardown);
     178  
     179    return g_test_run ();
     180  }