From f7604f117f323c7e5c4a42e13fa691ec0a2a182e Mon Sep 17 00:00:00 2001 From: John Ralls Date: Mon, 5 Dec 2011 00:06:28 +0000 Subject: [PATCH] [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 --- src/test-core/test-stuff.c | 96 ++++++++++++++++++++++++++++++-------- src/test-core/test-stuff.h | 67 +++++++++++++++++++------- 2 files changed, 128 insertions(+), 35 deletions(-) diff --git a/src/test-core/test-stuff.c b/src/test-core/test-stuff.c index b576d6dfd5..099180ad5b 100644 --- a/src/test-core/test-stuff.c +++ b/src/test-core/test-stuff.c @@ -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 diff --git a/src/test-core/test-stuff.h b/src/test-core/test-stuff.h index cbcbfaa7f2..a477a605b1 100644 --- a/src/test-core/test-stuff.h +++ b/src/test-core/test-stuff.h @@ -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 */