[libpeas] Implemented log hooks in libtesting-util



commit 74e0511c85390fe910fbe819403ae47400fe9292
Author: Garrett Regier <alias301 gmail com>
Date:   Sat Mar 5 00:37:10 2011 -0800

    Implemented log hooks in libtesting-util
    
    This allow us to add hooks for warning and criticals that are required
    during tests.
    
    These have several advantages over globally allowed patterns in that they
    avoid crossover issues when a test expects a warning but another
    test's pattern causes it to not be emitted. They are required to be triggered
    which avoids old patterns from being kept around when no longer needed.
    Most importantly they allow us to test that libpeas handles errors correctly.

 libpeas/peas-engine.c                     |   10 +++
 tests/libpeas/engine.c                    |    3 +
 tests/libpeas/extension-set.c             |   10 +--
 tests/libpeas/plugin-info.c               |   11 +--
 tests/libpeas/testing/testing-extension.c |  114 +++++++++--------------------
 tests/testing-util/testing-util.c         |  105 ++++++++++++++++++++++----
 tests/testing-util/testing-util.h         |   11 ++-
 7 files changed, 151 insertions(+), 113 deletions(-)
---
diff --git a/libpeas/peas-engine.c b/libpeas/peas-engine.c
index 15b902b..fd3885b 100644
--- a/libpeas/peas-engine.c
+++ b/libpeas/peas-engine.c
@@ -1036,6 +1036,11 @@ peas_engine_create_extension_valist (PeasEngine     *engine,
   GParameter *parameters;
   PeasExtension *exten;
 
+  g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (peas_plugin_info_is_loaded (info), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type), FALSE);
+
   type_struct = _g_type_struct_ref (extension_type);
 
   if (!_valist_to_parameter_list (extension_type, type_struct, first_property,
@@ -1093,6 +1098,11 @@ peas_engine_create_extension (PeasEngine     *engine,
   va_list var_args;
   PeasExtension *exten;
 
+  g_return_val_if_fail (PEAS_IS_ENGINE (engine), NULL);
+  g_return_val_if_fail (info != NULL, NULL);
+  g_return_val_if_fail (peas_plugin_info_is_loaded (info), NULL);
+  g_return_val_if_fail (G_TYPE_IS_INTERFACE (extension_type), FALSE);
+
   va_start (var_args, first_property);
   exten = peas_engine_create_extension_valist (engine, info, extension_type,
                                                first_property, var_args);
diff --git a/tests/libpeas/engine.c b/tests/libpeas/engine.c
index 79a09d1..5139d8f 100644
--- a/tests/libpeas/engine.c
+++ b/tests/libpeas/engine.c
@@ -362,6 +362,9 @@ test_engine_enable_loader (PeasEngine *engine)
    * a plugin if it's loader is not enabled.
    */
 
+  testing_util_push_log_hook ("*Could not find loader 'disabled'*");
+  testing_util_push_log_hook ("*Could not find loader 'invalid'*");
+
   info = peas_engine_get_plugin_info (engine, "disabled-loader");
 
   g_assert (!peas_engine_load_plugin (engine, info));
diff --git a/tests/libpeas/extension-set.c b/tests/libpeas/extension-set.c
index 84000c5..e46efcb 100644
--- a/tests/libpeas/extension-set.c
+++ b/tests/libpeas/extension-set.c
@@ -179,15 +179,11 @@ test_extension_set_call_valid (TestFixture *fixture)
 static void
 test_extension_set_call_invalid (TestFixture *fixture)
 {
+  testing_util_push_log_hook ("*Method 'PeasActivatable.invalid' not found*");
+
   test_extension_set_activate (fixture);
 
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_extension_set_call (fixture->extension_set, "invalid", NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*Method 'PeasActivatable.invalid' not found*");
+  peas_extension_set_call (fixture->extension_set, "invalid", NULL);
 }
 
 int
diff --git a/tests/libpeas/plugin-info.c b/tests/libpeas/plugin-info.c
index 58f0d66..7a2c837 100644
--- a/tests/libpeas/plugin-info.c
+++ b/tests/libpeas/plugin-info.c
@@ -129,19 +129,16 @@ test_plugin_info_has_dep (PeasEngine *engine)
 {
   PeasPluginInfo *info;
 
+  testing_util_push_log_hook ("*assertion `module_name != NULL' failed");
+
   info = peas_engine_get_plugin_info (engine, "full-info");
 
   g_assert (peas_plugin_info_has_dependency (info, "something"));
   g_assert (peas_plugin_info_has_dependency (info, "something-else"));
   g_assert (!peas_plugin_info_has_dependency (info, "does-not-exist"));
 
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_plugin_info_has_dependency (info, NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*CRITICAL*");
+
+  peas_plugin_info_has_dependency (info, NULL);
 
 
   info = peas_engine_get_plugin_info (engine, "min-info");
diff --git a/tests/libpeas/testing/testing-extension.c b/tests/libpeas/testing/testing-extension.c
index 5ae51a3..33e45db 100644
--- a/tests/libpeas/testing/testing-extension.c
+++ b/tests/libpeas/testing/testing-extension.c
@@ -61,7 +61,7 @@ testing_extension_test_teardown_ (TestingExtensionFixture_ *fixture,
 
 void
 testing_extension_test_runner_  (TestingExtensionFixture_ *fixture,
-                               gconstpointer             data)
+                                 gconstpointer             data)
 {
   ((void (*) (PeasEngine *engine)) data) (fixture->engine);
 }
@@ -99,6 +99,8 @@ testing_extension_provides_invalid_ (PeasEngine *engine)
 {
   PeasPluginInfo *info;
 
+  testing_util_push_log_hook ("*assertion `G_TYPE_IS_INTERFACE (*)' failed");
+
   info = peas_engine_get_plugin_info (engine, extension_plugin);
 
   /* Not loaded */
@@ -108,23 +110,11 @@ testing_extension_provides_invalid_ (PeasEngine *engine)
   g_assert (peas_engine_load_plugin (engine, info));
 
   /* Invalid GType */
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_engine_provides_extension (engine, info, G_TYPE_INVALID);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*CRITICAL*");
+  peas_engine_provides_extension (engine, info, G_TYPE_INVALID);
 
 
   /* GObject but not a GInterface */
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_engine_provides_extension (engine, info, PEAS_TYPE_ENGINE);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*CRITICAL*");
+  peas_engine_provides_extension (engine, info, PEAS_TYPE_ENGINE);
 
 
   /* Does not implement this GType */
@@ -156,51 +146,37 @@ void
 testing_extension_create_invalid_ (PeasEngine *engine)
 {
   PeasPluginInfo *info;
+  PeasExtension *extension;
+
+  testing_util_push_log_hook ("*assertion `peas_plugin_info_is_loaded (*)' failed");
+  testing_util_push_log_hook ("*assertion `G_TYPE_IS_INTERFACE (*)' failed");
+  testing_util_push_log_hook ("*does not provide a 'IntrospectionUnimplemented' extension");
 
   info = peas_engine_get_plugin_info (engine, extension_plugin);
 
   /* Not loaded */
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_engine_create_extension (engine, info,
-                                    INTROSPECTION_TYPE_CALLABLE,
-                                    NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
+  extension = peas_engine_create_extension (engine, info,
+                                            INTROSPECTION_TYPE_CALLABLE,
+                                            NULL);
+  g_assert (!PEAS_IS_EXTENSION (extension));
 
   g_assert (peas_engine_load_plugin (engine, info));
 
   /* Invalid GType */
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_engine_create_extension (engine, info, G_TYPE_INVALID, NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*CRITICAL*");
+  extension = peas_engine_create_extension (engine, info, G_TYPE_INVALID, NULL);
+  g_assert (!PEAS_IS_EXTENSION (extension));
 
 
   /* GObject but not a GInterface */
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_engine_create_extension (engine, info, PEAS_TYPE_ENGINE, NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*CRITICAL*");
+  extension = peas_engine_create_extension (engine, info, PEAS_TYPE_ENGINE, NULL);
+  g_assert (!PEAS_IS_EXTENSION (extension));
 
 
   /* Does not implement this GType */
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_engine_create_extension (engine, info,
-                                    INTROSPECTION_TYPE_UNIMPLEMENTED,
-                                    NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*WARNING*");
+  extension = peas_engine_create_extension (engine, info,
+                                            INTROSPECTION_TYPE_UNIMPLEMENTED,
+                                            NULL);
+  g_assert (!PEAS_IS_EXTENSION (extension));
 }
 
 void
@@ -224,6 +200,8 @@ testing_extension_call_invalid_ (PeasEngine *engine)
   PeasPluginInfo *info;
   PeasExtension *extension;
 
+  testing_util_push_log_hook ("*Method 'IntrospectionCallable.invalid' not found*");
+
   info = peas_engine_get_plugin_info (engine, extension_plugin);
 
   g_assert (peas_engine_load_plugin (engine, info));
@@ -232,13 +210,7 @@ testing_extension_call_invalid_ (PeasEngine *engine)
                                             INTROSPECTION_TYPE_CALLABLE,
                                             NULL);
 
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      peas_extension_call (extension, "invalid", NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*Method 'IntrospectionCallable.invalid' not found*");
+  peas_extension_call (extension, "invalid", NULL);
 
   g_object_unref (extension);
 }
@@ -362,6 +334,9 @@ testing_extension_properties_construct_only_ (PeasEngine *engine)
   PeasExtension *extension;
   gchar *construct_only;
 
+  testing_util_push_log_hook ("*property \"construct-only\" * "
+                              "can't be set after construction");
+
   info = peas_engine_get_plugin_info (engine, extension_plugin);
 
   FILE *saved_stdout = stdout;
@@ -377,18 +352,11 @@ testing_extension_properties_construct_only_ (PeasEngine *engine)
                                             "construct-only", "my-construct-only",
                                             NULL);
 
-
   g_object_get (extension, "construct-only", &construct_only, NULL);
   g_assert_cmpstr (construct_only, ==, "my-construct-only");
   g_free (construct_only);
 
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      g_object_set (extension, "construct-only", "other-construct-only", NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*WARNING*");
+  g_object_set (extension, "construct-only", "other-construct-only", NULL);
 
   g_object_unref (extension);
 }
@@ -400,6 +368,8 @@ testing_extension_properties_read_only_ (PeasEngine *engine)
   PeasExtension *extension;
   gchar *read_only;
 
+  testing_util_push_log_hook ("*property `read-only' * is not writable");
+
   info = peas_engine_get_plugin_info (engine, extension_plugin);
 
   g_assert (peas_engine_load_plugin (engine, info));
@@ -412,13 +382,7 @@ testing_extension_properties_read_only_ (PeasEngine *engine)
   g_assert_cmpstr (read_only, ==, "read-only");
   g_free (read_only);
 
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      g_object_set (extension, "read-only", "my-read-only", NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*WARNING*");
+  g_object_set (extension, "read-only", "my-read-only", NULL);
 
   g_object_unref (extension);
 }
@@ -428,6 +392,9 @@ testing_extension_properties_write_only_ (PeasEngine *engine)
 {
   PeasPluginInfo *info;
   PeasExtension *extension;
+  gchar *write_only = NULL;
+
+  testing_util_push_log_hook ("*property `write-only' * is not readable");
 
   info = peas_engine_get_plugin_info (engine, extension_plugin);
 
@@ -437,16 +404,7 @@ testing_extension_properties_write_only_ (PeasEngine *engine)
                                             INTROSPECTION_TYPE_PROPERTIES,
                                             NULL);
 
-  if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR))
-    {
-      gchar *write_only = NULL;
-
-      g_object_get (extension, "write-only", &write_only, NULL);
-      exit (0);
-    }
-  g_test_trap_assert_failed ();
-  g_test_trap_assert_stderr ("*WARNING*");
-
+  g_object_get (extension, "write-only", &write_only, NULL);
   g_object_set (extension, "write-only", "my-write-only", NULL);
 
   g_object_unref (extension);
diff --git a/tests/testing-util/testing-util.c b/tests/testing-util/testing-util.c
index 3a7a254..53523c8 100644
--- a/tests/testing-util/testing-util.c
+++ b/tests/testing-util/testing-util.c
@@ -32,13 +32,22 @@
 
 #include "testing-util.h"
 
+typedef struct {
+  const gchar *pattern;
+  gboolean hit;
+} LogHook;
+
 static PeasEngine *engine = NULL;
-static GLogFunc default_log_func;
+static GPtrArray *log_hooks = NULL;
 
-/* These are warning that just have to happen for testing
- * purposes and as such we don't want to abort on them.
+/* These are warnings and criticals that just have to happen
+ * for testing purposes and as such we don't want to abort on them.
+ *
+ * If the warnings are for specific tests use
+ * testing_push_log_hook() and testing_pop_log_hook() which
+ * will assert that the warning or critical actually happened.
  *
- * Would be nice if we could assert that they were...
+ * Don't bother putting errors in here as GLib always aborts on errors.
  */
 static const gchar *allowed_patterns[] = {
   "*does-not-exist*",
@@ -49,8 +58,6 @@ static const gchar *allowed_patterns[] = {
   "*Error loading *info-missing-iage.plugin*",
   "*Error loading *info-missing-module.plugin*",
   "*Error loading *info-missing-name.plugin*",
-  "*Could not find loader 'disabled'*",
-  "*Could not find loader 'invalid'*",
   "*Bad plugin file: *invalid.plugin*",
   "*Error loading *invalid.plugin*"
 };
@@ -61,33 +68,57 @@ log_handler (const gchar    *log_domain,
              const gchar    *message,
              gpointer        user_data)
 {
-  gint i;
+  guint i;
 
+  /* We always want to log debug, info and message logs */
   if ((log_level & G_LOG_LEVEL_DEBUG) != 0 ||
       (log_level & G_LOG_LEVEL_INFO) != 0 ||
       (log_level & G_LOG_LEVEL_MESSAGE) != 0)
     {
-      default_log_func (log_domain, log_level, message, user_data);
+      g_log_default_handler (log_domain, log_level, message, user_data);
       return;
     }
 
-  if ((log_level & G_LOG_LEVEL_CRITICAL) != 0 ||
-      (log_level & G_LOG_LEVEL_ERROR) != 0)
+  /* Don't bother trying to match errors as GLib always aborts on them */
+  if ((log_level & G_LOG_LEVEL_ERROR) != 0)
     {
-      goto out;
+      g_log_default_handler (log_domain, log_level, message, user_data);
+
+      /* Call abort() as GLib may call G_BREAKPOINT() instead */
+      abort ();
+    }
+
+  for (i = 0; i < log_hooks->len; ++i)
+    {
+      LogHook *hook = g_ptr_array_index (log_hooks, i);
+
+      if (g_pattern_match_simple (hook->pattern, message))
+        {
+          hook->hit = TRUE;
+          return;
+        }
     }
 
+  /* Check the allowed_patterns after the log hooks to
+   * avoid issues when an allowed_pattern would match a hook
+   */
   for (i = 0; i < G_N_ELEMENTS (allowed_patterns); ++i)
     {
       if (g_pattern_match_simple (allowed_patterns[i], message))
         return;
     }
 
-out:
+  /* Warnings and criticals are not allowed to be unhandled */
+  if ((log_level & G_LOG_LEVEL_WARNING) != 0)
+    message = g_strdup_printf ("Unhandled warning: %s: %s", log_domain, message);
+  else
+    message = g_strdup_printf ("Unhandled critical: %s: %s", log_domain, message);
 
-  default_log_func (log_domain, log_level, message, user_data);
+  /* Use the default log handler directly to avoid recurse complaints */
+  g_log_default_handler (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR,
+                         message, user_data);
 
-  /* Make sure we abort for warnings */
+  /* The default handler does not actually abort */
   abort ();
 }
 
@@ -100,10 +131,10 @@ testing_util_init (void)
   if (initialized)
     return;
 
-  /* Don't always abort on warnings */
-  g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
+  /* Don't abort on warnings or criticals */
+  g_log_set_always_fatal (G_LOG_LEVEL_ERROR);
 
-  default_log_func = g_log_set_default_handler (log_handler, NULL);
+  g_log_set_default_handler (log_handler, NULL);
 
   g_irepository_prepend_search_path (BUILDDIR "/libpeas");
 
@@ -112,6 +143,8 @@ testing_util_init (void)
   g_irepository_require (g_irepository_get_default (), "Peas", "1.0", 0, &error);
   g_assert_no_error (error);
 
+  log_hooks = g_ptr_array_new_with_free_func (g_free);
+
   initialized = TRUE;
 }
 
@@ -145,6 +178,10 @@ testing_util_engine_free (PeasEngine *engine_)
       /* Make sure that at the end of every test the engine is freed */
       g_assert (engine == NULL);
     }
+
+  /* Pop the log hooks so the test cases don't have to */
+  while (log_hooks->len > 0)
+    testing_util_pop_log_hook ();
 }
 
 int
@@ -154,6 +191,8 @@ testing_util_run_tests (void)
 
   retval = g_test_run ();
 
+  g_ptr_array_unref (log_hooks);
+
   /* Cannot call this with atexit() because
    * gcov does not register that it was called.
    */
@@ -161,3 +200,35 @@ testing_util_run_tests (void)
 
   return retval;
 }
+
+void
+testing_util_push_log_hook (const gchar *pattern)
+{
+  LogHook *hook;
+
+  g_return_if_fail (log_hooks != NULL);
+  g_return_if_fail (pattern != NULL && *pattern != '\0');
+
+  hook = g_new (LogHook, 1);
+  hook->pattern = pattern;
+  hook->hit = FALSE;
+
+  g_ptr_array_add (log_hooks, hook);
+}
+
+/* Optional - see testing_util_engine_free() */
+void
+testing_util_pop_log_hook (void)
+{
+  LogHook *hook;
+
+  g_return_if_fail (log_hooks != NULL);
+  g_return_if_fail (log_hooks->len > 0);
+  
+  hook = g_ptr_array_index (log_hooks, log_hooks->len - 1);
+
+  if (!hook->hit)
+    g_error ("Log hook was not triggered: '%s'", hook->pattern);
+
+  g_ptr_array_remove_index (log_hooks, log_hooks->len - 1);
+}
diff --git a/tests/testing-util/testing-util.h b/tests/testing-util/testing-util.h
index bc88b9b..5329050 100644
--- a/tests/testing-util/testing-util.h
+++ b/tests/testing-util/testing-util.h
@@ -26,12 +26,15 @@
 
 G_BEGIN_DECLS
 
-void        testing_util_init        (void);
+void        testing_util_init          (void);
 
-PeasEngine *testing_util_engine_new  (void);
-void        testing_util_engine_free (PeasEngine *engine);
+PeasEngine *testing_util_engine_new    (void);
+void        testing_util_engine_free   (PeasEngine *engine);
 
-int         testing_util_run_tests   (void);
+int         testing_util_run_tests     (void);
+
+void        testing_util_push_log_hook (const gchar *pattern);
+void        testing_util_pop_log_hook  (void);
 
 G_END_DECLS
 



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