[Testing] Provide list error handler, rename some functions

Add an GTestFatalFunc which can examine a list of error conditions and suppress those messages and prevent their exiting while passing through any unexpected errors.

Rename test_silent_logger to test_null_handler and change it to a GTestFatalFunc (it can still be used as a GLogFunc).

Rename test_handle_faults to test_checked_handler and improve its flow and message display. It no longer asserts on a bad match.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@21668 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
John Ralls 2011-12-05 00:06:28 +00:00
parent 609a23e431
commit f7604f117f
2 changed files with 128 additions and 35 deletions

View File

@ -358,37 +358,95 @@ get_random_string_in_array(const char* str_list[])
return str_list[num];
}
void
test_silent_logger( const char *log_domain, GLogLevelFlags log_level,
gboolean
test_null_handler (const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data )
{
//Silent, remember?
return;
return FALSE;
}
static gchar*
test_log_level (GLogLevelFlags flags)
{
const gchar *message[] = {"RECURSIVE", "FATAL", "ERROR", "CRITICAL",
"WARNING","MESSAGE", "INFO", "DEBUG"};
guint i = 0, last = 0, max_bit = 7;
gchar *msg = NULL;
for (i; i <= max_bit; i++)
if (flags & 1 << i)
{
gchar *tmp_msg = msg;
gchar *sep = (last < 2 ? " " : "|");
last = i;
msg = (tmp_msg ? g_strjoin (sep, tmp_msg, message[i], NULL)
: g_strdup (message[i]));
if (tmp_msg)
g_free (tmp_msg);
}
if (msg == NULL)
msg = g_strdup ("");
return msg;
}
static GList *message_queue = NULL;
void
test_add_error (TestErrorStruct *error)
{
message_queue = g_list_append (message_queue, error);
}
void
test_clear_error_list (void)
{
g_list_free (message_queue);
message_queue = NULL;
}
gboolean
test_handle_faults( const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data )
test_list_handler (const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data )
{
GList *list = g_list_first (message_queue);
const guint fatal = G_LOG_FLAG_FATAL;
while (list)
{
TestErrorStruct *error = (TestErrorStruct*)list->data;
if (!g_strcmp0 (log_domain, error->log_domain)
&& ((log_level | fatal) == (error->log_level | fatal))
&& !g_strcmp0 (msg, error->msg))
return FALSE;
list = g_list_next (list);
}
/* No list or no matches, fall through */
return test_checked_handler (log_domain, log_level, msg, user_data);
}
gboolean
test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data )
{
TestErrorStruct *tdata = (TestErrorStruct*)user_data;
if (tdata == NULL)
if ((tdata == NULL)
|| (tdata->log_domain != NULL
&& g_strcmp0 (tdata->log_domain, log_domain))
|| (tdata->log_level && tdata->log_level != log_level)
|| (tdata->msg && g_strcmp0 (tdata->msg, msg)))
{
g_printf("Received Log Message %s\n", msg);
gchar *level = test_log_level (log_level);
g_printf ( "<%s> (%s) %s\n", level, log_domain, msg);
g_free (level);
g_assert (log_level ^ G_LOG_FLAG_FATAL);
return FALSE;
}
/* If it's a lower loglevel than we expected, report it and move on */
if (tdata->log_level && log_level < tdata->log_level)
{
g_printf ("Received Log Message %s\n", msg);
return FALSE;
}
if (tdata->log_domain != NULL)
g_assert_cmpstr (tdata->log_domain, ==, log_domain);
if (tdata->log_level)
g_assert_cmpuint (tdata->log_level, ==, log_level);
if (tdata->log_level)
g_assert_cmpstr (tdata->msg, ==, msg);
return FALSE;
}
void

View File

@ -79,11 +79,21 @@ Otherwise, only failures are printed out.
}
/**
* Test Support
* Suppressing Expected Errors
*
* Struct and functions for unit test support:
* Intercept and report GLib error messages to test functions.
* Ensure that mock functions are called, and with the right data pointer
* Functions for suppressing expected errors during tests. Pass
*
* Note that you need to call both g_log_set_handler *and*
* 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.
*/
/**
* Struct to pass as user_data for the handlers. Setting a parameter
* to NULL or 0 will match any value in the error, so if you have the
* same message and log level being issued in two domains you can
* match both of them by setting log_domain = NULL.
*
*/
@ -95,22 +105,41 @@ typedef struct
} TestErrorStruct;
/**
* Pass this to g_test_log_set_fatal_handler(), setting user_data to
* a pointer to TestErrorStruct to intercept and handle expected
* error and warning messages. It will g_assert if an error is
* received which doesn't match the log_level, log_domain, and
* message in the struct (if they're set), or return FALSE to prevent
* the message from aborting. Be sure to g_free() the
* TestErrorData:msg after you're done testing it.
* 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
* member matches anything.
*/
gboolean test_handle_faults( const char *log_domain, GLogLevelFlags log_level,
gboolean test_checked_handler (const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data);
/**
* When you know you're going to get a useless log message, pass this
* to g_log_set_default_handler to shut it up.
* Just returns FALSE or suppresses the message regardless of what the
* error is. Use this only as a last resort.
*/
void test_silent_logger( const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data );
gboolean test_null_handler (const char *log_domain, GLogLevelFlags log_level,
const gchar *msg, gpointer user_data );
/**
* Maintains an internal list of TestErrorStructs which are each
* checked by the list handler. If an error matches any entry on the
* list, test_list_handler will return FALSE, blocking the error from
* halting the program.
*
* Call test_add_error for each TestErrorStruct to check against and
* test_clear_error_list when you no longer expect the errors.
*/
void test_add_error (TestErrorStruct *error);
void test_clear_error_list (void);
/**
* Checks received errors against the list created by
* test_add_error. If the list is empty or nothing matches, passes
* control on to test_checked_handler, giving the opportunity for an
* additional check that's not in the list (set user_data to NULL if
* you want test_checked_handler to immediately print the error).
*/
gboolean test_list_handler (const char *log_domain,
GLogLevelFlags log_level,
const gchar *msg, gpointer user_data );
/**
* Call this from a mock object to indicate that the mock has in fact
* been called
@ -237,4 +266,10 @@ TestSignal test_signal_new (QofInstance *entity, QofEventId eventType,
void test_signal_assert_hits (TestSignal sig, guint hits);
void test_signal_free (TestSignal sig);
/* For Scheme testing access:
void gnc_log_init_filename_special (gchar *filename);
void gnc_log_shutdown (void);
void gnc_log_set_handler (guint logdomain, gchar *logdomain, GLogFunc * func, gpointer data);
*/
#endif /* TEST_STUFF_H */