r23138 - gnucash/trunk/src/test-core - [Testing] Create log handlers in one step, clean up in teardown

John Ralls jralls at code.gnucash.org
Fri Aug 16 12:44:09 EDT 2013


Author: jralls
Date: 2013-08-16 12:44:08 -0400 (Fri, 16 Aug 2013)
New Revision: 23138
Trac: http://svn.gnucash.org/trac/changeset/23138

Modified:
   gnucash/trunk/src/test-core/unittest-support.c
   gnucash/trunk/src/test-core/unittest-support.h
Log:
[Testing] Create log handlers in one step, clean up in teardown

New functions to simplify creating log handlers for expected log
messages, especially fatal message handlers. Handlers are tracked in the
Fixture and automatically freed during teardown.

Modified: gnucash/trunk/src/test-core/unittest-support.c
===================================================================
--- gnucash/trunk/src/test-core/unittest-support.c	2013-08-16 16:43:58 UTC (rev 23137)
+++ gnucash/trunk/src/test-core/unittest-support.c	2013-08-16 16:44:08 UTC (rev 23138)
@@ -1,6 +1,6 @@
 /********************************************************************
  * unittest-support.c: Support structures for GLib Unit Testing     *
- * Copyright 2011-12 John Ralls <jralls at ceridwen.us>		    *
+ * Copyright 2011-13 John Ralls <jralls at ceridwen.us>		    *
  * Copyright 2011 Muslim Chochlov <muslim.chochlov at gmail.com>       *
  *                                                                  *
  * This program is free software; you can redistribute it and/or    *
@@ -33,7 +33,64 @@
 } TestStruct;
 
 static TestStruct tdata;
+static gboolean
+test_checked_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
+			    const gchar *msg, gpointer user_data);
+static gboolean
+test_list_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
+			 const gchar *msg, gpointer user_data);
 
+TestErrorStruct*
+test_error_struct_new (gchar *log_domain, GLogLevelFlags log_level, gchar *msg)
+{
+    TestErrorStruct *err = g_slice_new0 (TestErrorStruct);
+    err->log_domain = g_strdup (log_domain);
+    err->log_level = log_level;
+    err->msg = g_strdup (msg);
+}
+
+void
+test_error_struct_free (TestErrorStruct *err)
+{
+    g_free (err->log_domain);
+    g_free (err->msg);
+    g_slice_free (TestErrorStruct, err);
+}
+
+GSList*
+test_log_set_handler (GSList *list, TestErrorStruct *error, GLogFunc handler)
+{
+    TestLogHandler *hdlr = g_slice_new0 (TestLogHandler);
+    hdlr->error = error;
+    hdlr->handler = g_log_set_handler (error->log_domain, error->log_level,
+				       handler, error);
+    return g_slist_prepend (list, hdlr);
+}
+
+GSList*
+test_log_set_fatal_handler (GSList *list, TestErrorStruct *error,
+			    GLogFunc handler)
+{
+    TestLogHandler *hdlr = g_slice_new0 (TestLogHandler);
+    GTestLogFatalFunc f_hdlr = handler == (GLogFunc)test_list_handler ?
+	(GTestLogFatalFunc)test_list_nohit_handler :
+	(GTestLogFatalFunc)test_checked_nohit_handler;
+    hdlr->error = error;
+    hdlr->handler = g_log_set_handler (error->log_domain, error->log_level,
+				       handler, error);
+    g_test_log_set_fatal_handler (f_hdlr, error);
+    return g_slist_prepend (list, hdlr);
+}
+
+void
+test_free_log_handler (gpointer item)
+{
+    TestLogHandler *handler = (TestLogHandler*)item;
+    g_log_remove_handler (handler->error->log_domain, handler->handler);
+    test_error_struct_free (handler->error);
+    g_slice_free (TestLogHandler, handler);
+}
+
 gboolean
 test_null_handler (const char *log_domain, GLogLevelFlags log_level,
                    const gchar *msg, gpointer user_data )
@@ -84,9 +141,9 @@
     message_queue = NULL;
 }
 
-gboolean
-test_list_handler (const char *log_domain, GLogLevelFlags log_level,
-                   const gchar *msg, gpointer user_data )
+static gboolean
+do_test_list_handler (const char *log_domain, GLogLevelFlags log_level,
+		      const gchar *msg, gpointer user_data, gboolean hits)
 {
     GList *list = g_list_first (message_queue);
     const guint fatal = G_LOG_FLAG_FATAL;
@@ -98,7 +155,8 @@
                 && ((log_level | fatal) == (error->log_level | fatal))
                 && !g_strcmp0 (msg, error->msg))
 	{
-	    ++(error->hits);
+	    if (hits)
+		++(error->hits);
             return FALSE;
 	}
         list = g_list_next (list);
@@ -107,11 +165,24 @@
     return test_checked_handler (log_domain, log_level, msg, user_data);
 }
 
+gboolean
+test_list_handler (const char *log_domain, GLogLevelFlags log_level,
+		      const gchar *msg, gpointer user_data)
+{
+    return do_test_list_handler (log_domain, log_level, msg, user_data, TRUE);
+}
 
 gboolean
-test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
-                      const gchar *msg, gpointer user_data )
+test_list_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
+			 const gchar *msg, gpointer user_data)
 {
+    return do_test_list_handler (log_domain, log_level, msg, user_data, TRUE);
+}
+
+static gboolean
+do_test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
+			 const gchar *msg, gpointer user_data, gboolean hits)
+{
     TestErrorStruct *tdata = (TestErrorStruct*)user_data;
 
     if ((tdata == NULL)
@@ -126,12 +197,27 @@
         g_assert (log_level ^ G_LOG_FLAG_FATAL);
         return FALSE;
     }
-    ++(tdata->hits);
+    if (hits)
+	++(tdata->hits);
     return FALSE;
 
 }
 
 gboolean
+test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
+                      const gchar *msg, gpointer user_data )
+{
+    do_test_checked_handler (log_domain, log_level, msg, user_data, TRUE);
+}
+
+static gboolean
+test_checked_nohit_handler (const char *log_domain, GLogLevelFlags log_level,
+			    const gchar *msg, gpointer user_data )
+{
+    do_test_checked_handler (log_domain, log_level, msg, user_data, FALSE);
+}
+
+gboolean
 test_log_handler (const char *log_domain, GLogLevelFlags log_level,
                       const gchar *msg, gpointer user_data )
 {

Modified: gnucash/trunk/src/test-core/unittest-support.h
===================================================================
--- gnucash/trunk/src/test-core/unittest-support.h	2013-08-16 16:43:58 UTC (rev 23137)
+++ gnucash/trunk/src/test-core/unittest-support.h	2013-08-16 16:44:08 UTC (rev 23138)
@@ -59,6 +59,21 @@
  * g_test_log_set_fatal_handler to both avoid the assertion and
  * suppress the error message. The callbacks work in either role, just
  * cast them appropriately for the use.
+ *
+ * To simplify the process a bit and make sure that everything gets
+ * cleaned up at the end of each test, add a GSList for handlers to
+ * your Fixture and set it to NULL in setup(), then call
+ * g_slist_free_full() on it with test_free_log_handler as the
+ * function. Create new TestErrorStruct instances with
+ * test_error_struct_new, and pass that along with your handler of
+ * choice to test_log_set_handler or test_log_set_fatal_handler. This
+ * is much simpler, as teardown will clean everything up for you and
+ * you need call only those functions. As an added bonus, the hit
+ * count won't be doubled as it is if you do everything by hand.
+ *
+ * NB: If you have more than one fatal error in a test function be
+ * sure to use the test_list_handler: You can have only one fatal
+ * handler.
  */
 
 /**
@@ -78,6 +93,75 @@
 } TestErrorStruct;
 
 /**
+ * Convenience function to create an error struct. If you use this
+ * with test_set_log_handler it will get cleaned up at tesrdown,
+ * otherwise call test_error_free() at the end of your test function.
+ *
+ * NB: If you need to change the message, be sure to free the old one
+ * and to allocate the new one on the stack.
+ *
+ * @param log_domain: The string representing the domain of the log message
+ * @param log_level: The GLogLevelFlags for the message
+ * @param msg: The exact error message that the logger will emit
+ * @return: A TestErrorStruct *
+ */
+TestErrorStruct* test_error_struct_new (gchar *log_domain,
+					GLogLevelFlags log_level,
+					gchar *msg);
+
+/**
+ * Free a TestErrorStruct created with test_error_struct_new
+ * @param error: The TestErrorStruct to be freed
+ */
+void test_error_struct_free (TestErrorStruct *);
+
+typedef struct
+{
+    TestErrorStruct *error;
+    gint handler;
+    gboolean list_handler;
+} TestLogHandler;
+
+
+/**
+ * Set a log handler and add it to a GList for removal at teardown
+ *
+ * Don't pass a NULL TestErrorStruct! It's needed to set the
+ * parameters for g_log_set_handler. Use a TestErrorStruct created
+ * with test_error_struct_new() or you'll have errors with freeing it
+ * in teardown.
+ *
+ * @param handler_list: A GSList of LogHandlers
+ * @param error: A TestErrorStruct with the necessary data
+ * @param handler: The Handler to set the data with
+ * @return: The new GSList pointer.
+ */
+GSList *test_log_set_handler (GSList *list, TestErrorStruct *error,
+			      GLogFunc handler);
+
+/**
+ * Set a log handler and add it to a GList for removal at teardown;
+ * also set the fatal handler so that the test program doesn't abort
+ * for fatal log messages. If a test function has more than one fatal
+ * message, be sure to use the test_list_handler!
+ *
+ * Don't pass a NULL TestErrorStruct! It's needed to set the
+ * parameters for g_log_set_handler. Use a TestErrorStruct created
+ * with test_error_struct_new() or you'll have errors with freeing it
+ * in teardown.
+ *
+ * @param handler_list: A GSList of LogHandlers
+ * @param error: A TestErrorStruct with the necessary data
+ * @param handler: The Handler to set the data with
+ * @return: The new GSList pointer.
+ */
+GSList *test_log_set_fatal_handler (GSList *list, TestErrorStruct *error,
+			      GLogFunc handler);
+
+/* Clears all the log handlers. Pass this to g_slist_free() in teardown */
+void test_free_log_handler (gpointer item);
+
+/**
  * Check the user_data against the actual error and assert on any
  * differences.  Displays the error (and asserts if G_LOG_FLAG_FATAL
  * is TRUE) if NULL is passed as user_data, but a NULL or 0 value



More information about the gnucash-changes mailing list