[libpeas] Add support for embedded C plugins



commit b19f1cea0fdc5b91ed6ef6d40ff67f9dcd217eda
Author: Garrett Regier <garrettregier gmail com>
Date:   Fri Nov 20 19:50:48 2015 -0800

    Add support for embedded C plugins
    
    This adds the new key Embedded to the .plugin
    file which specifies the function to call instead
    of peas_register_types to perform that same job.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=721693

 .gitignore                                         |    2 +
 configure.ac                                       |    1 +
 docs/reference/libpeas-docs.sgml                   |    4 +
 docs/reference/libpeas-sections.txt                |    2 +
 libpeas/peas-engine.c                              |   78 ++++++++++--
 libpeas/peas-object-module.c                       |  122 ++++++++++++++----
 libpeas/peas-object-module.h                       |    3 +
 libpeas/peas-plugin-info-priv.h                    |    1 +
 libpeas/peas-plugin-info.c                         |   59 ++++++++-
 libpeas/peas-plugin-loader-c.c                     |   22 ++-
 tests/libpeas/Makefile.am                          |    2 +
 tests/libpeas/extension-c.c                        |   65 ++++++++++
 tests/libpeas/plugins/Makefile.am                  |    2 +-
 tests/libpeas/plugins/embedded/Makefile.am         |   55 ++++++++
 .../embedded/embedded-invalid-loader.plugin        |    8 +
 .../embedded/embedded-missing-symbol.plugin        |    7 +
 tests/libpeas/plugins/embedded/embedded-plugin.c   |  135 ++++++++++++++++++++
 tests/libpeas/plugins/embedded/embedded-plugin.h   |   52 ++++++++
 .../plugins/embedded/embedded.gresource.xml        |    7 +
 tests/libpeas/plugins/embedded/embedded.plugin     |    7 +
 tests/libpeas/testing/testing.c                    |   19 +++-
 21 files changed, 606 insertions(+), 47 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index a8aba8f..73a8b4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,6 +87,8 @@ Makefile.in
 /tests/libpeas/extension-python3
 /tests/libpeas/extension-set
 /tests/libpeas/plugin-info
+/tests/libpeas/plugins/embedded/embedded-resources.c
+/tests/libpeas/plugins/embedded/embedded-resources.h
 /tests/libpeas/plugins/extension-python/extension-python.gschema.xml
 /tests/libpeas/plugins/extension-python/extension-python.plugin
 /tests/libpeas/plugins/extension-python/extension-python.py
diff --git a/configure.ac b/configure.ac
index c838361..6a7028d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -556,6 +556,7 @@ po/Makefile.in
 tests/Makefile
 tests/libpeas/Makefile
 tests/libpeas/plugins/Makefile
+tests/libpeas/plugins/embedded/Makefile
 tests/libpeas/plugins/extension-c/Makefile
 tests/libpeas/plugins/extension-lua/Makefile
 tests/libpeas/plugins/extension-python/Makefile
diff --git a/docs/reference/libpeas-docs.sgml b/docs/reference/libpeas-docs.sgml
index 44e5c5f..0e96245 100644
--- a/docs/reference/libpeas-docs.sgml
+++ b/docs/reference/libpeas-docs.sgml
@@ -59,6 +59,10 @@
     <title>Index of new symbols in 1.14</title>
     <xi:include href="xml/api-index-1.14.xml"><xi:fallback /></xi:include>
   </index>
+  <index id="api-index-1-18" role="1.18">
+    <title>Index of new symbols in 1.18</title>
+    <xi:include href="xml/api-index-1.18.xml"><xi:fallback /></xi:include>
+  </index>
 
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 </book>
diff --git a/docs/reference/libpeas-sections.txt b/docs/reference/libpeas-sections.txt
index ff80d31..610a163 100644
--- a/docs/reference/libpeas-sections.txt
+++ b/docs/reference/libpeas-sections.txt
@@ -152,10 +152,12 @@ PEAS_OBJECT_MODULE_GET_CLASS
 PeasObjectModulePrivate
 peas_object_module_new
 peas_object_module_new_full
+peas_object_module_new_embedded
 peas_object_module_create_object
 peas_object_module_get_library
 peas_object_module_get_module_name
 peas_object_module_get_path
+peas_object_module_get_symbol
 peas_object_module_provides_object
 </SECTION>
 
diff --git a/libpeas/peas-engine.c b/libpeas/peas-engine.c
index 4a2782b..ccee8a6 100644
--- a/libpeas/peas-engine.c
+++ b/libpeas/peas-engine.c
@@ -161,10 +161,10 @@ load_plugin_info (PeasEngine  *engine,
 }
 
 static void
-load_dir_real (PeasEngine  *engine,
-               const gchar *module_dir,
-               const gchar *data_dir,
-               guint        recursions)
+load_file_dir_real (PeasEngine  *engine,
+                    const gchar *module_dir,
+                    const gchar *data_dir,
+                    guint        recursions)
 {
   GError *error = NULL;
   GDir *d;
@@ -188,7 +188,7 @@ load_dir_real (PeasEngine  *engine,
       if (g_file_test (filename, G_FILE_TEST_IS_DIR))
         {
           if (recursions > 0)
-            load_dir_real (engine, filename, data_dir, recursions - 1);
+            load_file_dir_real (engine, filename, data_dir, recursions - 1);
         }
       else if (g_str_has_suffix (dirent, ".plugin"))
         {
@@ -201,6 +201,67 @@ load_dir_real (PeasEngine  *engine,
   g_dir_close (d);
 }
 
+static void
+load_resource_dir_real (PeasEngine  *engine,
+                        const gchar *module_dir,
+                        const gchar *data_dir,
+                        guint        recursions)
+{
+  guint i;
+  const gchar *module_path;
+  gchar **children;
+  GError *error = NULL;
+
+  g_debug ("Loading %s/*.plugin...", module_dir);
+
+  module_path = module_dir + strlen ("resource://");
+  children = g_resources_enumerate_children (module_path,
+                                             G_RESOURCE_LOOKUP_FLAGS_NONE,
+                                             &error);
+
+  if (error != NULL)
+    {
+      g_debug ("%s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  for (i = 0; children[i] != NULL; ++i)
+    {
+      gboolean is_dir;
+      gchar *child;
+
+      is_dir = g_str_has_suffix (children[i], "/");
+
+      if (is_dir && recursions == 0)
+        continue;
+
+      if (!is_dir && !g_str_has_suffix (children[i], ".plugin"))
+        continue;
+
+      child = g_build_path ("/", module_dir, children[i], NULL);
+
+      if (is_dir)
+        load_resource_dir_real (engine, child, data_dir, recursions - 1);
+      else
+        load_plugin_info (engine, child, module_dir, data_dir);
+
+      g_free (child);
+    }
+
+  g_strfreev (children);
+}
+
+static void
+load_dir_real (PeasEngine *engine,
+               SearchPath *sp)
+{
+  if (!g_str_has_prefix (sp->module_dir, "resource://"))
+    load_file_dir_real (engine, sp->module_dir, sp->data_dir, 1);
+  else
+    load_resource_dir_real (engine, sp->module_dir, sp->data_dir, 1);
+}
+
 /**
  * peas_engine_rescan_plugins:
  * @engine: A #PeasEngine.
@@ -229,10 +290,7 @@ peas_engine_rescan_plugins (PeasEngine *engine)
 
   /* Go and read everything from the provided search paths */
   for (item = priv->search_paths; item != NULL; item = item->next)
-    {
-      SearchPath *sp = (SearchPath *) item->data;
-      load_dir_real (engine, sp->module_dir, sp->data_dir, 1);
-    }
+    load_dir_real (engine, (SearchPath *) item->data);
 
   g_object_thaw_notify (G_OBJECT (engine));
 }
@@ -256,7 +314,7 @@ peas_engine_insert_search_path (PeasEngine  *engine,
   priv->search_paths = g_list_insert (priv->search_paths, sp, position);
 
   g_object_freeze_notify (G_OBJECT (engine));
-  load_dir_real (engine, sp->module_dir, sp->data_dir, 1);
+  load_dir_real (engine, sp);
   g_object_thaw_notify (G_OBJECT (engine));
 }
 
diff --git a/libpeas/peas-object-module.c b/libpeas/peas-object-module.c
index a6ae729..fbd81f2 100644
--- a/libpeas/peas-object-module.c
+++ b/libpeas/peas-object-module.c
@@ -48,6 +48,7 @@ enum {
   PROP_0,
   PROP_MODULE_NAME,
   PROP_PATH,
+  PROP_SYMBOL,
   PROP_RESIDENT,
   PROP_LOCAL_LINKAGE,
   N_PROPERTIES
@@ -70,6 +71,7 @@ struct _PeasObjectModulePrivate {
 
   gchar *path;
   gchar *module_name;
+  gchar *symbol;
 
   guint resident : 1;
   guint local_linkage : 1;
@@ -91,27 +93,39 @@ peas_object_module_load (GTypeModule *gmodule)
 {
   PeasObjectModule *module = PEAS_OBJECT_MODULE (gmodule);
   PeasObjectModulePrivate *priv = GET_PRIV (module);
-  GModuleFlags flags = 0;
-  gchar *path;
-
-  if (priv->local_linkage)
-    flags = G_MODULE_BIND_LOCAL;
 
-  path = g_module_build_path (priv->path, priv->module_name);
-  g_return_val_if_fail (path != NULL, FALSE);
+  g_return_val_if_fail (priv->module_name != NULL, FALSE);
 
-  /* g_module_build_path() will add G_MODULE_SUFFIX to the path,
-   * however g_module_open() will only try to load the libtool archive
-   * if there is no suffix specified. So we remove G_MODULE_SUFFIX here
-   * which allows uninstalled builds to load plugins as well, as there
-   * is only the .la file in the build directory.
-   */
-  if (G_MODULE_SUFFIX[0] != '\0' && g_str_has_suffix (path, "." G_MODULE_SUFFIX))
-    path[strlen (path) - strlen (G_MODULE_SUFFIX) - 1] = '\0';
+  if (priv->path == NULL)
+    {
+      g_return_val_if_fail (priv->resident, FALSE);
+      g_return_val_if_fail (!priv->local_linkage, FALSE);
 
-  /* Bind symbols immediately to avoid errors long after loading */
-  priv->library = g_module_open (path, flags);
-  g_free (path);
+      priv->library = g_module_open (NULL, 0);
+    }
+  else
+    {
+      gchar *path;
+      GModuleFlags flags = 0;
+
+      path = g_module_build_path (priv->path, priv->module_name);
+
+      /* g_module_build_path() will add G_MODULE_SUFFIX to the path,
+       * however g_module_open() will only try to load the libtool archive
+       * if there is no suffix specified. So we remove G_MODULE_SUFFIX here
+       * which allows uninstalled builds to load plugins as well, as there
+       * is only the .la file in the build directory.
+       */
+      if (G_MODULE_SUFFIX[0] != '\0' && g_str_has_suffix (path, "." G_MODULE_SUFFIX))
+        path[strlen (path) - strlen (G_MODULE_SUFFIX) - 1] = '\0';
+
+      if (priv->local_linkage)
+        flags = G_MODULE_BIND_LOCAL;
+
+      /* Bind symbols immediately to avoid errors long after loading */
+      priv->library = g_module_open (path, flags);
+      g_free (path);
+    }
 
   if (priv->library == NULL)
     {
@@ -122,12 +136,11 @@ peas_object_module_load (GTypeModule *gmodule)
     }
 
   /* Extract the required symbol from the library */
-  if (!g_module_symbol (priv->library,
-                        "peas_register_types",
+  if (!g_module_symbol (priv->library, priv->symbol,
                         (gpointer) &priv->register_func))
     {
-      g_warning ("Failed to get 'peas_register_types' for module '%s': %s",
-                 priv->module_name, g_module_error ());
+      g_warning ("Failed to get '%s' for module '%s': %s",
+                 priv->symbol, priv->module_name, g_module_error ());
       g_module_close (priv->library);
 
       return FALSE;
@@ -138,8 +151,8 @@ peas_object_module_load (GTypeModule *gmodule)
    */
   if (priv->register_func == NULL)
     {
-      g_warning ("Invalid 'peas_register_types' in module '%s'",
-                 priv->module_name);
+      g_warning ("Invalid '%s' in module '%s'",
+                 priv->symbol, priv->module_name);
       g_module_close (priv->library);
 
       return FALSE;
@@ -194,6 +207,7 @@ peas_object_module_finalize (GObject *object)
 
   g_free (priv->path);
   g_free (priv->module_name);
+  g_free (priv->symbol);
   g_array_unref (priv->implementations);
 
   G_OBJECT_CLASS (peas_object_module_parent_class)->finalize (object);
@@ -216,6 +230,9 @@ peas_object_module_get_property (GObject    *object,
     case PROP_PATH:
       g_value_set_string (value, priv->path);
       break;
+    case PROP_SYMBOL:
+      g_value_set_string (value, priv->symbol);
+      break;
     case PROP_RESIDENT:
       g_value_set_boolean (value, priv->resident);
       break;
@@ -247,6 +264,9 @@ peas_object_module_set_property (GObject      *object,
     case PROP_PATH:
       priv->path = g_value_dup_string (value);
       break;
+    case PROP_SYMBOL:
+      priv->symbol = g_value_dup_string (value);
+      break;
     case PROP_RESIDENT:
       priv->resident = g_value_get_boolean (value);
       break;
@@ -292,6 +312,15 @@ peas_object_module_class_init (PeasObjectModuleClass *klass)
                          G_PARAM_CONSTRUCT_ONLY |
                          G_PARAM_STATIC_STRINGS);
 
+  properties[PROP_SYMBOL] =
+    g_param_spec_string ("symbol",
+                         "Symbol",
+                         "The registration symbol to use for this module",
+                         "peas_register_types",
+                         G_PARAM_READWRITE |
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS);
+
   properties[PROP_RESIDENT] =
     g_param_spec_boolean ("resident",
                           "Resident",
@@ -377,6 +406,31 @@ peas_object_module_new_full (const gchar *module_name,
 }
 
 /**
+ * peas_object_module_new_embedded: (skip)
+ * @module_name: The module name.
+ *
+ * Creates a new #PeasObjectModule for an embedded plugin.
+ *
+ * Return value: a new #PeasObjectModule.
+ *
+ * Since: 1.18
+ */
+PeasObjectModule *
+peas_object_module_new_embedded (const gchar *module_name,
+                                 const gchar *symbol)
+{
+  g_return_val_if_fail (module_name != NULL && *module_name != '\0', NULL);
+  g_return_val_if_fail (symbol != NULL && *symbol != '\0', NULL);
+
+  return PEAS_OBJECT_MODULE (g_object_new (PEAS_TYPE_OBJECT_MODULE,
+                                           "module-name", module_name,
+                                           "symbol", symbol,
+                                           "resident", TRUE,
+                                           "local-linkage", FALSE,
+                                           NULL));
+}
+
+/**
  * peas_object_module_create_object: (skip)
  * @module: A #PeasObjectModule.
  * @interface: The #GType of the extension interface.
@@ -484,6 +538,26 @@ peas_object_module_get_module_name (PeasObjectModule *module)
 }
 
 /**
+ * peas_object_module_get_symbol: (skip)
+ * @module: A #PeasObjectModule.
+ *
+ * Gets the symbol name used to register extension implementations.
+ *
+ * Return value: the symbol name.
+ *
+ * Since: 1.18
+ */
+const gchar *
+peas_object_module_get_symbol (PeasObjectModule *module)
+{
+  PeasObjectModulePrivate *priv = GET_PRIV (module);
+
+  g_return_val_if_fail (PEAS_IS_OBJECT_MODULE (module), NULL);
+
+  return priv->symbol;
+}
+
+/**
  * peas_object_module_get_library: (skip)
  * @module: A #PeasObjectModule.
  *
diff --git a/libpeas/peas-object-module.h b/libpeas/peas-object-module.h
index 9f7e164..70a0265 100644
--- a/libpeas/peas-object-module.h
+++ b/libpeas/peas-object-module.h
@@ -91,6 +91,8 @@ PeasObjectModule   *peas_object_module_new_full               (const gchar
                                                                const gchar      *path,
                                                                gboolean          resident,
                                                                gboolean          local_linkage);
+PeasObjectModule   *peas_object_module_new_embedded           (const gchar      *module_name,
+                                                               const gchar      *symbol);
 
 GObject            *peas_object_module_create_object          (PeasObjectModule *module,
                                                                GType             interface,
@@ -101,6 +103,7 @@ gboolean            peas_object_module_provides_object        (PeasObjectModule
 
 const gchar        *peas_object_module_get_path               (PeasObjectModule *module);
 const gchar        *peas_object_module_get_module_name        (PeasObjectModule *module);
+const gchar        *peas_object_module_get_symbol             (PeasObjectModule *module);
 
 GModule            *peas_object_module_get_library            (PeasObjectModule *module);
 
diff --git a/libpeas/peas-plugin-info-priv.h b/libpeas/peas-plugin-info-priv.h
index e4b2fd3..2359df5 100644
--- a/libpeas/peas-plugin-info-priv.h
+++ b/libpeas/peas-plugin-info-priv.h
@@ -37,6 +37,7 @@ struct _PeasPluginInfo {
   gchar *data_dir;
 
   gint loader_id;
+  gchar *embedded;
   gchar *module_name;
   gchar **dependencies;
 
diff --git a/libpeas/peas-plugin-info.c b/libpeas/peas-plugin-info.c
index b30e82b..6fb7ce6 100644
--- a/libpeas/peas-plugin-info.c
+++ b/libpeas/peas-plugin-info.c
@@ -85,6 +85,7 @@ _peas_plugin_info_unref (PeasPluginInfo *info)
   g_free (info->filename);
   g_free (info->module_dir);
   g_free (info->data_dir);
+  g_free (info->embedded);
   g_free (info->module_name);
   g_strfreev (info->dependencies);
   g_free (info->name);
@@ -124,19 +125,42 @@ _peas_plugin_info_new (const gchar *filename,
                        const gchar *data_dir)
 {
   gsize i;
+  gboolean is_resource;
   gchar *loader = NULL;
   gchar **strv, **keys;
   PeasPluginInfo *info;
   GKeyFile *plugin_file;
+  GBytes *bytes = NULL;
   GError *error = NULL;
 
   g_return_val_if_fail (filename != NULL, NULL);
 
+  is_resource = g_str_has_prefix (filename, "resource://");
+
   info = g_new0 (PeasPluginInfo, 1);
   info->refcount = 1;
 
   plugin_file = g_key_file_new ();
-  if (!g_key_file_load_from_file (plugin_file, filename,
+  
+  if (is_resource)
+    {
+      bytes = g_resources_lookup_data (filename + strlen ("resource://"),
+                                       G_RESOURCE_LOOKUP_FLAGS_NONE,
+                                       &error);
+    }
+  else
+    {
+      gchar *content;
+      gsize length;
+
+      if (g_file_get_contents (filename, &content, &length, &error))
+        bytes = g_bytes_new_take (content, length);
+    }
+
+  if (bytes == NULL ||
+      !g_key_file_load_from_data (plugin_file,
+                                  g_bytes_get_data (bytes, NULL),
+                                  g_bytes_get_size (bytes),
                                   G_KEY_FILE_NONE, &error))
     {
       g_warning ("Bad plugin file '%s': %s", filename, error->message);
@@ -183,7 +207,31 @@ _peas_plugin_info_new (const gchar *filename,
         }
     }
 
-  g_free (loader);
+  /* Get Embedded */
+  info->embedded = g_key_file_get_string (plugin_file, "Plugin",
+                                          "Embedded", NULL);
+  if (info->embedded != NULL)
+    {
+      if (info->loader_id != PEAS_UTILS_C_LOADER_ID)
+        {
+          g_warning ("Bad plugin file '%s': embedded plugins "
+                     "must use the C plugin loader", filename);
+          goto error;
+        }
+
+      if (!is_resource)
+        {
+          g_warning ("Bad plugin file '%s': embedded plugins "
+                     "must be a resource", filename);
+          goto error;
+        }
+    }
+  else if (is_resource)
+    {
+      g_warning ("Bad plugin file '%s': resource plugins must be embedded",
+                 filename);
+      goto error;
+    }
 
   /* Get the dependency list */
   info->dependencies = g_key_file_get_string_list (plugin_file,
@@ -259,11 +307,14 @@ _peas_plugin_info_new (const gchar *filename,
 
   g_strfreev (keys);
 
+  g_free (loader);
+  g_bytes_unref (bytes);
   g_key_file_free (plugin_file);
 
   info->filename = g_strdup (filename);
   info->module_dir = g_strdup (module_dir);
-  info->data_dir = g_build_filename (data_dir, info->module_name, NULL);
+  info->data_dir = g_build_path (is_resource ? "/" : G_DIR_SEPARATOR_S,
+                                 data_dir, info->module_name, NULL);
 
   /* If we know nothing about the availability of the plugin,
      set it as available */
@@ -273,10 +324,12 @@ _peas_plugin_info_new (const gchar *filename,
 
 error:
 
+  g_free (info->embedded);
   g_free (loader);
   g_free (info->module_name);
   g_free (info->name);
   g_free (info);
+  g_clear_pointer (&bytes, g_bytes_unref);
   g_key_file_free (plugin_file);
 
   return NULL;
diff --git a/libpeas/peas-plugin-loader-c.c b/libpeas/peas-plugin-loader-c.c
index 59942d0..a476eb4 100644
--- a/libpeas/peas-plugin-loader-c.c
+++ b/libpeas/peas-plugin-loader-c.c
@@ -65,13 +65,21 @@ peas_plugin_loader_c_load (PeasPluginLoader *loader,
       module_name = peas_plugin_info_get_module_name (info);
       module_dir = peas_plugin_info_get_module_dir (info);
 
-      /* Force all C modules to be resident in case they
-       * use libraries that do not deal well with reloading.
-       * Furthermore, we use local linkage to improve module isolation.
-       */
-      info->loader_data = peas_object_module_new_full (module_name,
-                                                       module_dir,
-                                                       TRUE, TRUE);
+      if (info->embedded != NULL)
+        {
+          info->loader_data = peas_object_module_new_embedded (module_name,
+                                                               info->embedded);
+        }
+      else
+        {
+          /* Force all C modules to be resident in case they
+           * use libraries that do not deal well with reloading.
+           * Furthermore, we use local linkage to improve module isolation.
+           */
+          info->loader_data = peas_object_module_new_full (module_name,
+                                                           module_dir,
+                                                           TRUE, TRUE);
+        }
 
       if (!g_type_module_use (G_TYPE_MODULE (info->loader_data)))
         g_clear_object (&info->loader_data);
diff --git a/tests/libpeas/Makefile.am b/tests/libpeas/Makefile.am
index 6d6696f..fd55197 100644
--- a/tests/libpeas/Makefile.am
+++ b/tests/libpeas/Makefile.am
@@ -25,6 +25,8 @@ TEST_PROGS += \
        extension-set   \
        plugin-info
 
+extension_c_LDADD = $(LDADD) $(builddir)/plugins/embedded/libembedded.la
+
 if ENABLE_LUA51
 TEST_PROGS               += extension-lua51
 extension_lua51_SOURCES   = extension-lua.c
diff --git a/tests/libpeas/extension-c.c b/tests/libpeas/extension-c.c
index 05ea14d..4b6834c 100644
--- a/tests/libpeas/extension-c.c
+++ b/tests/libpeas/extension-c.c
@@ -24,9 +24,69 @@
 #endif
 
 #include "libpeas/peas.h"
+#include "libpeas/peas-plugin-info-priv.h"
 
 #include "testing/testing-extension.h"
 #include "introspection/introspection-base.h"
+#include "plugins/embedded/embedded-plugin.h"
+#include "plugins/embedded/embedded-resources.h"
+
+
+static void
+test_extension_c_embedded (PeasEngine *engine)
+{
+  PeasPluginInfo *info;
+  PeasExtension *extension;
+
+  info = peas_engine_get_plugin_info (engine, "embedded");
+
+  /* Check that the various data is correct */
+  g_assert (!peas_plugin_info_is_loaded (info));
+  g_assert (peas_plugin_info_is_available (info, NULL));
+  g_assert (!peas_plugin_info_is_builtin (info));
+  g_assert (!peas_plugin_info_is_hidden (info));
+  g_assert_cmpstr (peas_plugin_info_get_module_name (info), ==, "embedded");
+  g_assert_cmpstr (peas_plugin_info_get_module_dir (info), ==,
+                   "resource:///org/gnome/libpeas/tests/plugins");
+  g_assert_cmpstr (peas_plugin_info_get_data_dir (info), ==,
+                   "resource:///org/gnome/libpeas/tests/plugins/embedded");
+  g_assert_cmpstr (info->embedded, ==,
+                   "testing_embedded_plugin_register_types");
+
+  /* Check that we can load and unload the plugin multiple times */
+  g_assert (peas_engine_load_plugin (engine, info));
+  g_assert (peas_plugin_info_is_loaded (info));
+  g_assert (peas_engine_load_plugin (engine, info));
+  g_assert (peas_plugin_info_is_loaded (info));
+  g_assert (peas_engine_unload_plugin (engine, info));
+  g_assert (!peas_plugin_info_is_loaded (info));
+  g_assert (peas_engine_unload_plugin (engine, info));
+  g_assert (!peas_plugin_info_is_loaded (info));
+  g_assert (peas_engine_load_plugin (engine, info));
+  g_assert (peas_plugin_info_is_loaded (info));
+
+  extension = peas_engine_create_extension (engine, info,
+                                            PEAS_TYPE_ACTIVATABLE,
+                                            NULL);
+
+  g_assert (TESTING_IS_EMBEDDED_PLUGIN (extension));
+
+  g_object_unref (extension);
+}
+
+static void
+test_extension_c_embedded_missing_symbol (PeasEngine *engine)
+{
+  PeasPluginInfo *info;
+
+  testing_util_push_log_hook ("Failed to get '*does_not_exist*' "
+                              "for module 'embedded-missing-symbol':*");
+  testing_util_push_log_hook ("Error loading plugin 'embedded-missing-symbol'");
+
+  info = peas_engine_get_plugin_info (engine, "embedded-missing-symbol");
+
+  g_assert (!peas_engine_load_plugin (engine, info));
+}
 
 static void
 test_extension_c_instance_refcount (PeasEngine     *engine,
@@ -120,6 +180,9 @@ main (int   argc,
 {
   testing_init (&argc, &argv);
 
+  /* Automatic resource registration has issues here */
+  embedded_register_resource ();
+
   /* Only test the basics */
   testing_extension_basic ("c");
 
@@ -128,6 +191,8 @@ main (int   argc,
    */
   testing_extension_callable ("c");
 
+  EXTENSION_TEST (c, "embedded", embedded);
+  EXTENSION_TEST (c, "embedded-missing-symbol", embedded_missing_symbol);
   EXTENSION_TEST (c, "instance-refcount", instance_refcount);
   EXTENSION_TEST (c, "nonexistent", nonexistent);
   EXTENSION_TEST (c, "local-linkage", local_linkage);
diff --git a/tests/libpeas/plugins/Makefile.am b/tests/libpeas/plugins/Makefile.am
index 2dfddb2..5db17a9 100644
--- a/tests/libpeas/plugins/Makefile.am
+++ b/tests/libpeas/plugins/Makefile.am
@@ -1,6 +1,6 @@
 include $(top_srcdir)/tests/Makefile.plugin
 
-SUBDIRS = extension-c
+SUBDIRS = embedded extension-c
 
 if ENABLE_LUA51
 SUBDIRS += extension-lua
diff --git a/tests/libpeas/plugins/embedded/Makefile.am b/tests/libpeas/plugins/embedded/Makefile.am
new file mode 100644
index 0000000..1123d79
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/Makefile.am
@@ -0,0 +1,55 @@
+include $(top_srcdir)/tests/Makefile.plugin
+
+AM_CPPFLAGS = \
+       -I$(top_srcdir)         \
+       $(PEAS_CFLAGS)          \
+       $(WARN_CFLAGS)          \
+       $(DISABLE_DEPRECATED)
+
+noinst_LTLIBRARIES = libembedded.la
+
+libembedded_la_SOURCES = \
+       embedded-plugin.c       \
+       embedded-plugin.h       \
+       embedded-resources.c    \
+       embedded-resources.h
+
+libembedded_la_LIBADD  = \
+       $(PEAS_LIBS)                            \
+       $(top_builddir)/libpeas/libpeas-1.0.la
+
+embedded_resources_deps = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir "$(srcdir)" --generate-dependencies 
"$(srcdir)/embedded.gresource.xml")
+embedded_resources_c_deps = $(srcdir)/embedded.gresource.xml $(embedded_resources_deps)
+
+embedded-resources.h: $(embedded_resources_c_deps)
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES)           \
+               --target="$@"                           \
+               --sourcedir="$(srcdir)"                 \
+               --generate                              \
+               --c-name="embedded"                     \
+               --manual-register                       \
+               --internal                              \
+               "$(srcdir)/embedded.gresource.xml"
+
+embedded-resources.c: $(embedded_resources_c_deps) embedded-resources.h
+       $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES)           \
+               --target="$@"                           \
+               --sourcedir="$(srcdir)"                 \
+               --generate                              \
+               --c-name="embedded"                     \
+               --manual-register                       \
+               --internal                              \
+               "$(srcdir)/embedded.gresource.xml"
+
+noinst_PLUGIN = \
+       embedded.gresource.xml          \
+       embedded.plugin                 \
+       embedded-invalid-loader.plugin  \
+       embedded-missing-symbol.plugin
+
+EXTRA_DIST = $(noinst_PLUGIN)
+
+CLEANFILES = \
+       embedded-resources.c    \
+       embedded-resources.h
+
diff --git a/tests/libpeas/plugins/embedded/embedded-invalid-loader.plugin 
b/tests/libpeas/plugins/embedded/embedded-invalid-loader.plugin
new file mode 100644
index 0000000..413e663
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/embedded-invalid-loader.plugin
@@ -0,0 +1,8 @@
+[Plugin]
+Module=embedded-invalid-loader
+Embedded=peas_engine_new
+Loader=python
+Name=Embedded Invalid Loader
+Description=This plugin is embedded with an invalid loader.
+Authors=Garrett Regier
+Copyright=Copyright © 2015 Garrett Regier
diff --git a/tests/libpeas/plugins/embedded/embedded-missing-symbol.plugin 
b/tests/libpeas/plugins/embedded/embedded-missing-symbol.plugin
new file mode 100644
index 0000000..7b04c23
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/embedded-missing-symbol.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=embedded-missing-symbol
+Embedded=embedded_missing_symbol__this_symbol_does_not_exist__register_types
+Name=Embedded Missing Symbol
+Description=This plugin is embedded with a missing symbol.
+Authors=Garrett Regier
+Copyright=Copyright © 2015 Garrett Regier
diff --git a/tests/libpeas/plugins/embedded/embedded-plugin.c 
b/tests/libpeas/plugins/embedded/embedded-plugin.c
new file mode 100644
index 0000000..945d6f4
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/embedded-plugin.c
@@ -0,0 +1,135 @@
+/*
+ * embedded-plugin.c
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2015 - Garrett Regier
+ *
+ * libpeas is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libpeas is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+
+#include <libpeas/peas.h>
+
+#include "embedded-plugin.h"
+
+typedef struct {
+  GObject *object;
+} TestingEmbeddedPluginPrivate;
+
+static void peas_activatable_iface_init (PeasActivatableInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (TestingEmbeddedPlugin,
+                        testing_embedded_plugin,
+                        PEAS_TYPE_EXTENSION_BASE,
+                        0,
+                        G_ADD_PRIVATE (TestingEmbeddedPlugin)
+                        G_IMPLEMENT_INTERFACE (PEAS_TYPE_ACTIVATABLE,
+                                               peas_activatable_iface_init))
+
+#define GET_PRIV(o) \
+  (testing_embedded_plugin_get_instance_private (o))
+
+enum {
+  PROP_0,
+  PROP_OBJECT
+};
+
+static void
+testing_embedded_plugin_set_property (GObject      *object,
+                                      guint         prop_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  TestingEmbeddedPlugin *plugin = TESTING_EMBEDDED_PLUGIN (object);
+  TestingEmbeddedPluginPrivate *priv = GET_PRIV (plugin);
+
+  switch (prop_id)
+    {
+    case PROP_OBJECT:
+      priv->object = g_value_get_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+testing_embedded_plugin_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  TestingEmbeddedPlugin *plugin = TESTING_EMBEDDED_PLUGIN (object);
+  TestingEmbeddedPluginPrivate *priv = GET_PRIV (plugin);
+
+  switch (prop_id)
+    {
+    case PROP_OBJECT:
+      g_value_set_object (value, priv->object);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+testing_embedded_plugin_init (TestingEmbeddedPlugin *plugin)
+{
+}
+
+static void
+testing_embedded_plugin_activate (PeasActivatable *activatable)
+{
+}
+
+static void
+testing_embedded_plugin_deactivate (PeasActivatable *activatable)
+{
+}
+
+static void
+testing_embedded_plugin_class_init (TestingEmbeddedPluginClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->set_property = testing_embedded_plugin_set_property;
+  object_class->get_property = testing_embedded_plugin_get_property;
+
+  g_object_class_override_property (object_class, PROP_OBJECT, "object");
+}
+
+static void
+peas_activatable_iface_init (PeasActivatableInterface *iface)
+{
+  iface->activate = testing_embedded_plugin_activate;
+  iface->deactivate = testing_embedded_plugin_deactivate;
+}
+
+G_MODULE_EXPORT void
+testing_embedded_plugin_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              PEAS_TYPE_ACTIVATABLE,
+                                              TESTING_TYPE_EMBEDDED_PLUGIN);
+}
diff --git a/tests/libpeas/plugins/embedded/embedded-plugin.h 
b/tests/libpeas/plugins/embedded/embedded-plugin.h
new file mode 100644
index 0000000..b4111e5
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/embedded-plugin.h
@@ -0,0 +1,52 @@
+/*
+ * embedded-plugin.h
+ * This file is part of libpeas
+ *
+ * Copyright (C) 2015 - Garrett Regier
+ *
+ * libpeas is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libpeas is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
+ */
+
+#ifndef __TESTING_EMBEDDED_PLUGIN_H__
+#define __TESTING_EMBEDDED_PLUGIN_H__
+
+#include <libpeas/peas.h>
+
+G_BEGIN_DECLS
+
+#define TESTING_TYPE_EMBEDDED_PLUGIN         (testing_embedded_plugin_get_type ())
+#define TESTING_EMBEDDED_PLUGIN(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), TESTING_TYPE_EMBEDDED_PLUGIN, 
TestingEmbeddedPlugin))
+#define TESTING_EMBEDDED_PLUGIN_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), TESTING_TYPE_EMBEDDED_PLUGIN, 
TestingEmbeddedPlugin))
+#define TESTING_IS_EMBEDDED_PLUGIN(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), TESTING_TYPE_EMBEDDED_PLUGIN))
+#define TESTING_IS_EMBEDDED_PLUGIN_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), TESTING_TYPE_EMBEDDED_PLUGIN))
+#define TESTING_EMBEDDED_PLUGIN_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TESTING_TYPE_EMBEDDED_PLUGIN, 
TestingEmbeddedPluginClass))
+
+typedef struct _TestingEmbeddedPlugin         TestingEmbeddedPlugin;
+typedef struct _TestingEmbeddedPluginClass    TestingEmbeddedPluginClass;
+
+struct _TestingEmbeddedPlugin {
+  PeasExtensionBase parent_instance;
+};
+
+struct _TestingEmbeddedPluginClass {
+  PeasExtensionBaseClass parent_class;
+};
+
+GType                testing_embedded_plugin_get_type       (void) G_GNUC_CONST;
+G_MODULE_EXPORT void testing_embedded_plugin_register_types (PeasObjectModule *module);
+
+G_END_DECLS
+
+#endif /* __TESTING_EMBEDDED_PLUGIN_H__ */
diff --git a/tests/libpeas/plugins/embedded/embedded.gresource.xml 
b/tests/libpeas/plugins/embedded/embedded.gresource.xml
new file mode 100644
index 0000000..e141934
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/embedded.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/org/gnome/libpeas/tests/plugins">
+    <file>embedded.plugin</file>
+    <file>embedded-missing-symbol.plugin</file>
+  </gresource>
+</gresources>
diff --git a/tests/libpeas/plugins/embedded/embedded.plugin b/tests/libpeas/plugins/embedded/embedded.plugin
new file mode 100644
index 0000000..13563bd
--- /dev/null
+++ b/tests/libpeas/plugins/embedded/embedded.plugin
@@ -0,0 +1,7 @@
+[Plugin]
+Module=embedded
+Embedded=testing_embedded_plugin_register_types
+Name=Embedded
+Description=A plugin that is embedded.
+Authors=Garrett Regier
+Copyright=Copyright © 2015 Garrett Regier
diff --git a/tests/libpeas/testing/testing.c b/tests/libpeas/testing/testing.c
index f71aec1..1d06dcd 100644
--- a/tests/libpeas/testing/testing.c
+++ b/tests/libpeas/testing/testing.c
@@ -76,10 +76,25 @@ testing_engine_new_full (gboolean nonglobal_loaders)
                               "*unkown-loader.plugin* does-not-exist");
   testing_util_push_log_hook ("*Error loading *unkown-loader.plugin*");
 
+  testing_util_push_log_hook ("Bad plugin file '"
+                              BUILDDIR "*/embedded*.plugin': "
+                              "embedded plugins must be a resource");
+  testing_util_push_log_hook ("Error loading '"
+                              BUILDDIR "*/embedded*.plugin'*");
+
+  testing_util_push_log_hook ("Bad plugin file '"
+                              BUILDDIR "*embedded-invalid-loader.plugin': "
+                              "embedded plugins must use the C plugin loader");
+
   /* Must be after pushing log hooks */
   engine = testing_util_engine_new_full (nonglobal_loaders);
-  peas_engine_add_search_path (engine, BUILDDIR "/tests/libpeas/plugins",
-                                       SRCDIR   "/tests/libpeas/plugins");
+
+  peas_engine_add_search_path (engine,
+                               "resource:///org/gnome/libpeas/"
+                               "tests/plugins", NULL);
+  peas_engine_add_search_path (engine,
+                               BUILDDIR "/tests/libpeas/plugins",
+                               SRCDIR   "/tests/libpeas/plugins");
 
   return engine;
 }


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