1 /*
2  * extension-set.c
3  * This file is part of libpeas
4  *
5  * Copyright (C) 2010 - Garrett Regier
6  *
7  * libpeas is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * libpeas is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <stdlib.h>
27 
28 #include <glib.h>
29 #include <glib-object.h>
30 #include <libpeas/peas.h>
31 
32 #include "testing/testing.h"
33 
34 typedef struct _TestFixture TestFixture;
35 
36 struct _TestFixture {
37   PeasEngine *engine;
38 };
39 
40 /* Have dependencies before the plugin that requires them */
41 static const gchar *loadable_plugins[] = {
42   "loadable", "has-dep", "self-dep"
43 };
44 
45 static void
extension_added_cb(PeasExtensionSet * extension_set,PeasPluginInfo * info,PeasExtension * extension,gint * active)46 extension_added_cb (PeasExtensionSet *extension_set,
47                     PeasPluginInfo   *info,
48                     PeasExtension    *extension,
49                     gint             *active)
50 {
51   ++(*active);
52 }
53 
54 static void
extension_removed_cb(PeasExtensionSet * extension_set,PeasPluginInfo * info,PeasExtension * extension,gint * active)55 extension_removed_cb (PeasExtensionSet *extension_set,
56                       PeasPluginInfo   *info,
57                       PeasExtension    *extension,
58                       gint             *active)
59 {
60   --(*active);
61 }
62 
63 static PeasExtensionSet *
testing_extension_set_new(PeasEngine * engine,gint * active)64 testing_extension_set_new (PeasEngine *engine,
65                            gint       *active)
66 {
67   gint i;
68   PeasPluginInfo *info;
69   PeasExtensionSet *extension_set;
70 
71   extension_set = peas_extension_set_new (engine,
72                                           PEAS_TYPE_ACTIVATABLE,
73                                           "object", NULL,
74                                           NULL);
75 
76   if (active == NULL)
77     {
78       active = g_new (gint, 1);
79       g_object_set_data_full (G_OBJECT (extension_set),
80                               "testing-extension-set-active", active,
81                               g_free);
82     }
83 
84   *active = 0;
85 
86   g_signal_connect (extension_set,
87                     "extension-added",
88                     G_CALLBACK (extension_added_cb),
89                     active);
90   g_signal_connect (extension_set,
91                     "extension-removed",
92                     G_CALLBACK (extension_removed_cb),
93                     active);
94 
95   peas_extension_set_foreach (extension_set,
96                               (PeasExtensionSetForeachFunc) extension_added_cb,
97                               active);
98 
99   for (i = 0; i < G_N_ELEMENTS (loadable_plugins); ++i)
100     {
101       g_assert_cmpint (*active, ==, i);
102 
103       info = peas_engine_get_plugin_info (engine, loadable_plugins[i]);
104       g_assert (peas_engine_load_plugin (engine, info));
105     }
106 
107   /* Load a plugin that does not provide a PeasActivatable */
108   info = peas_engine_get_plugin_info (engine, "extension-c");
109   g_assert (peas_engine_load_plugin (engine, info));
110 
111   g_assert_cmpint (*active, ==, G_N_ELEMENTS (loadable_plugins));
112 
113   return extension_set;
114 }
115 
116 static void
test_setup(TestFixture * fixture,gconstpointer data)117 test_setup (TestFixture   *fixture,
118             gconstpointer  data)
119 {
120   fixture->engine = testing_engine_new ();
121 }
122 
123 static void
test_teardown(TestFixture * fixture,gconstpointer data)124 test_teardown (TestFixture   *fixture,
125                gconstpointer  data)
126 {
127   testing_engine_free (fixture->engine);
128 }
129 
130 static void
test_runner(TestFixture * fixture,gconstpointer data)131 test_runner (TestFixture   *fixture,
132              gconstpointer  data)
133 {
134   ((void (*) (PeasEngine *engine)) data) (fixture->engine);
135 }
136 
137 static void
test_extension_set_create_valid(PeasEngine * engine)138 test_extension_set_create_valid (PeasEngine *engine)
139 {
140   PeasExtensionSet *extension_set;
141 
142   extension_set = peas_extension_set_new (engine,
143                                           PEAS_TYPE_ACTIVATABLE,
144                                           "object", NULL,
145                                           NULL);
146 
147   g_object_unref (extension_set);
148 }
149 
150 static void
valid_extension_added_cb(PeasExtensionSet * extension_set,PeasPluginInfo * info,PeasExtension * extension,GObject ** obj_ptr)151 valid_extension_added_cb (PeasExtensionSet *extension_set,
152                           PeasPluginInfo   *info,
153                           PeasExtension    *extension,
154                           GObject          **obj_ptr)
155 {
156   g_object_get (PEAS_ACTIVATABLE (extension), "object", obj_ptr, NULL);
157 }
158 
159 static void
test_extension_set_create_valid_with_properties(PeasEngine * engine)160 test_extension_set_create_valid_with_properties (PeasEngine *engine)
161 {
162   PeasPluginInfo *info;
163   PeasExtensionSet *extension_set;
164   GValue prop_value = G_VALUE_INIT;
165   GObject *obj, *obj_cmp;
166   const gchar *prop_names[1] = { "object" };
167 
168   obj = g_object_new (G_TYPE_OBJECT, NULL);
169   g_value_init (&prop_value, G_TYPE_OBJECT);
170   g_value_set_object (&prop_value, obj);
171 
172   extension_set = peas_extension_set_new_with_properties (engine,
173                                                           PEAS_TYPE_ACTIVATABLE,
174                                                           G_N_ELEMENTS (prop_names),
175                                                           prop_names,
176                                                           &prop_value);
177   g_signal_connect (extension_set,
178                     "extension-added",
179                     G_CALLBACK (valid_extension_added_cb),
180                     &obj_cmp);
181   info = peas_engine_get_plugin_info (engine, "builtin");
182 
183   g_assert (peas_engine_load_plugin (engine, info));
184   g_assert (obj == obj_cmp);
185 
186   g_assert (PEAS_IS_EXTENSION_SET (extension_set));
187   g_object_unref (extension_set);
188   g_value_unset (&prop_value);
189 }
190 
191 
192 static void
test_extension_set_create_invalid(PeasEngine * engine)193 test_extension_set_create_invalid (PeasEngine *engine)
194 {
195   PeasExtensionSet *extension_set;
196 
197   testing_util_push_log_hook ("*assertion*G_TYPE_IS_INTERFACE*failed");
198   testing_util_push_log_hook ("*type 'PeasActivatable' has no property named 'invalid-property'");
199 
200   /* Invalid GType */
201   extension_set = peas_extension_set_new (engine, G_TYPE_INVALID, NULL);
202   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
203 
204 
205   /* GObject but not a GInterface */
206   extension_set = peas_extension_set_new (engine, G_TYPE_OBJECT, NULL);
207   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
208 
209 
210   /* Interface does not have an 'invalid-property' property */
211   extension_set = peas_extension_set_new (engine,
212                                           PEAS_TYPE_ACTIVATABLE,
213                                           "invalid-property", "does-not-exist",
214                                           NULL);
215   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
216 }
217 
218 static void
test_extension_set_create_invalid_with_properties(PeasEngine * engine)219 test_extension_set_create_invalid_with_properties (PeasEngine *engine)
220 {
221   PeasExtensionSet *extension_set;
222   GValue prop_values[2] = { G_VALUE_INIT };
223   const gchar *prop_names[2] = { "object", NULL };
224   const gchar *prop_names_not_exist[1] = { "aleb" };
225   guint n_elements;
226 
227   testing_util_push_log_hook ("*property name*should not be NULL.");
228   testing_util_push_log_hook ("*assertion*G_TYPE_IS_INTERFACE*failed");
229   testing_util_push_log_hook ("*should be an initialized GValue.");
230   testing_util_push_log_hook ("*has no property named 'aleb'*");
231 
232   g_value_init (&prop_values[0], G_TYPE_POINTER);
233   g_value_set_pointer (&prop_values[0], NULL);
234 
235   /* Interface has a NULL property name*/
236   n_elements = G_N_ELEMENTS (prop_values);
237   extension_set = peas_extension_set_new_with_properties (engine,
238                                                           PEAS_TYPE_ACTIVATABLE,
239                                                           n_elements,
240                                                           prop_names,
241                                                           prop_values);
242   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
243   g_value_unset (&prop_values[0]);
244 
245   /* Invalid GType */
246   extension_set = peas_extension_set_new_with_properties (engine,
247                                                           G_TYPE_INVALID,
248                                                           0, NULL, NULL);
249   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
250 
251   /* Uninitialized GValue */
252   n_elements = 1;
253   extension_set = peas_extension_set_new_with_properties (engine,
254                                                           PEAS_TYPE_ACTIVATABLE,
255                                                           n_elements,
256                                                           prop_names,
257                                                           prop_values);
258   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
259 
260   /* Uninitialized GValue*/
261   g_value_init (&prop_values[0], G_TYPE_POINTER);
262   g_value_set_pointer (&prop_values[0], NULL);
263   n_elements = G_N_ELEMENTS (prop_names_not_exist);
264   extension_set = peas_extension_set_new_with_properties (engine,
265                                                           PEAS_TYPE_ACTIVATABLE,
266                                                           n_elements,
267                                                           prop_names_not_exist,
268                                                           prop_values);
269   g_assert (!PEAS_IS_EXTENSION_SET (extension_set));
270   g_value_unset (&prop_values[0]);
271 }
272 
273 
274 static void
test_extension_set_extension_added(PeasEngine * engine)275 test_extension_set_extension_added (PeasEngine *engine)
276 {
277   gint active;
278   PeasExtensionSet *extension_set;
279 
280   /* This will check that an extension is added
281    * as plugins are loaded and cause active to
282    * be synced with the number of added extensions
283    */
284   extension_set = testing_extension_set_new (engine, &active);
285 
286   g_object_unref (extension_set);
287 
288   /* Verify that freeing the extension
289    * set causes the extensions to be removed
290    */
291   g_assert_cmpint (active, ==, 0);
292 }
293 
294 static void
test_extension_set_extension_removed(PeasEngine * engine)295 test_extension_set_extension_removed (PeasEngine *engine)
296 {
297   gint i, active;
298   PeasPluginInfo *info;
299   PeasExtensionSet *extension_set;
300 
301   extension_set = testing_extension_set_new (engine, &active);
302 
303   /* Unload the plugin that does not provide a PeasActivatable */
304   info = peas_engine_get_plugin_info (engine, "extension-c");
305   g_assert (peas_engine_unload_plugin (engine, info));
306 
307   /* To keep deps in order */
308   for (i = G_N_ELEMENTS (loadable_plugins); i > 0; --i)
309     {
310       g_assert_cmpint (active, ==, i);
311 
312       info = peas_engine_get_plugin_info (engine, loadable_plugins[i - 1]);
313 
314       g_assert (peas_engine_unload_plugin (engine, info));
315     }
316 
317   g_assert_cmpint (active, ==, 0);
318 
319   g_object_unref (extension_set);
320 }
321 
322 static void
test_extension_set_get_extension(PeasEngine * engine)323 test_extension_set_get_extension (PeasEngine *engine)
324 {
325   PeasPluginInfo *info;
326   PeasExtension *extension;
327   PeasExtensionSet *extension_set;
328 
329   extension_set = testing_extension_set_new (engine, NULL);
330   info = peas_engine_get_plugin_info (engine, loadable_plugins[0]);
331 
332   extension = peas_extension_set_get_extension (extension_set, info);
333   g_assert (PEAS_IS_ACTIVATABLE (extension));
334 
335   g_object_add_weak_pointer (G_OBJECT (extension),
336                              (gpointer) &extension);
337   g_assert (peas_engine_unload_plugin (engine, info));
338 
339   g_assert (extension == NULL);
340   g_assert (peas_extension_set_get_extension (extension_set, info) == NULL);
341 
342   g_object_unref (extension_set);
343 }
344 
345 static void
test_extension_set_call_valid(PeasEngine * engine)346 test_extension_set_call_valid (PeasEngine *engine)
347 {
348   PeasExtensionSet *extension_set;
349 
350   extension_set = testing_extension_set_new (engine, NULL);
351 
352   g_assert (peas_extension_set_call (extension_set, "activate", NULL));
353 
354   g_object_unref (extension_set);
355 }
356 
357 static void
test_extension_set_call_invalid(PeasEngine * engine)358 test_extension_set_call_invalid (PeasEngine *engine)
359 {
360   PeasExtensionSet *extension_set;
361 
362   testing_util_push_log_hook ("Method 'PeasActivatable.invalid' was not found");
363 
364   extension_set = testing_extension_set_new (engine, NULL);
365 
366   g_assert (!peas_extension_set_call (extension_set, "invalid", NULL));
367 
368   g_object_unref (extension_set);
369 }
370 
371 static void
test_extension_set_foreach(PeasEngine * engine)372 test_extension_set_foreach (PeasEngine *engine)
373 {
374   gint count = 0;
375   PeasExtensionSet *extension_set;
376 
377   extension_set = testing_extension_set_new (engine, NULL);
378 
379   peas_extension_set_foreach (extension_set,
380                               (PeasExtensionSetForeachFunc) extension_added_cb,
381                               &count);
382 
383   g_assert_cmpint (count, ==, G_N_ELEMENTS (loadable_plugins));
384 
385   g_object_unref (extension_set);
386 }
387 
388 static void
ordering_cb(PeasExtensionSet * set,PeasPluginInfo * info,PeasExtension * extension,GList ** order)389 ordering_cb (PeasExtensionSet  *set,
390              PeasPluginInfo    *info,
391              PeasExtension     *extension,
392              GList            **order)
393 {
394   const gchar *order_module_name = (const gchar *) (*order)->data;
395 
396   g_assert_cmpstr (order_module_name, ==,
397                    peas_plugin_info_get_module_name (info));
398   *order = g_list_delete_link (*order, *order);
399 }
400 
401 static void
test_extension_set_ordering(PeasEngine * engine)402 test_extension_set_ordering (PeasEngine *engine)
403 {
404   guint i;
405   GList *foreach_order = NULL;
406   GList *dispose_order = NULL;
407   PeasExtensionSet *extension_set;
408 
409   for (i = 0; i < G_N_ELEMENTS (loadable_plugins); ++i)
410     {
411       /* Use descriptive names to make an assert more informative */
412       foreach_order = g_list_append (foreach_order,
413                                      (gpointer) loadable_plugins[i]);
414       dispose_order = g_list_prepend (dispose_order,
415                                       (gpointer) loadable_plugins[i]);
416     }
417 
418   extension_set = testing_extension_set_new (engine, NULL);
419 
420   peas_extension_set_foreach (extension_set,
421                               (PeasExtensionSetForeachFunc) ordering_cb,
422                               &foreach_order);
423   g_assert (foreach_order == NULL);
424 
425 
426   g_signal_connect (extension_set,
427                     "extension-removed",
428                     G_CALLBACK (ordering_cb),
429                     &dispose_order);
430   g_object_unref (extension_set);
431   g_assert (dispose_order == NULL);
432 }
433 
434 int
main(int argc,char ** argv)435 main (int    argc,
436       char **argv)
437 {
438   testing_init (&argc, &argv);
439 
440 #define TEST(path, ftest) \
441   g_test_add ("/extension-set/" path, TestFixture, \
442               (gpointer) test_extension_set_##ftest, \
443               test_setup, test_runner, test_teardown)
444 
445   TEST ("create-valid", create_valid);
446   TEST ("create-invalid", create_invalid);
447   TEST ("create-valid-with-properties", create_valid_with_properties);
448   TEST ("create-invalid-with-properties", create_invalid_with_properties);
449 
450   TEST ("extension-added", extension_added);
451   TEST ("extension-removed", extension_removed);
452 
453   TEST ("get-extension", get_extension);
454 
455   TEST ("call-valid", call_valid);
456   TEST ("call-invalid", call_invalid);
457 
458   TEST ("foreach", foreach);
459 
460   TEST ("ordering", ordering);
461 
462 #undef TEST
463 
464   return testing_run_tests ();
465 }
466