check: Add API to filter g_warning/g_critical etc

New API functions to filter log messages before they are processed by
GstCheck. This can be used to discard specific messages that are
accepted by the test or to add callbacks that test specific messages.

Default bevavior when no callback is given to a filter is to discard the
message, because it does not makes sense to have a filter with no
callback which does not discard; that would be a noop.

Discarded messages will in addition to bypass the GstCheck handling also
return to GLib that the message is not fatal if it occurs.

https://bugzilla.gnome.org/show_bug.cgi?id=773091
This commit is contained in:
Stian Selnes 2016-05-24 14:57:54 +02:00 committed by Tim-Philipp Müller
parent a533b6c2f9
commit 0c36e5766d
3 changed files with 234 additions and 2 deletions

View file

@ -74,6 +74,9 @@ LIBGSTCHECK_EXPORTED_FUNCS = \
gst_check_element_push_buffer \
gst_check_element_push_buffer_list \
gst_check_init \
gst_check_add_log_filter \
gst_check_remove_log_filter \
gst_check_clear_log_filter \
gst_check_message_error \
gst_check_run_suite \
gst_check_setup_element \

View file

@ -61,11 +61,204 @@ gboolean _gst_check_raised_critical = FALSE;
gboolean _gst_check_raised_warning = FALSE;
gboolean _gst_check_expecting_log = FALSE;
gboolean _gst_check_list_tests = FALSE;
static GQueue _gst_check_log_filters = G_QUEUE_INIT;
static GMutex _gst_check_log_filters_mutex;
struct _GstCheckLogFilter {
gchar *log_domain;
GLogLevelFlags log_level;
GRegex *regex;
GstCheckLogFilterFunc func;
gpointer user_data;
GDestroyNotify destroy;
};
static gboolean
gst_check_match_log_filter (const GstCheckLogFilter * filter,
const gchar * log_domain, GLogLevelFlags log_level, const gchar * message)
{
if (g_strcmp0 (log_domain, filter->log_domain) != 0)
return FALSE;
if ((log_level & filter->log_level) == 0)
return FALSE;
if (!g_regex_match (filter->regex, message, 0, NULL))
return FALSE;
return TRUE;
}
static GstCheckLogFilter *
gst_check_alloc_log_filter (const gchar * log_domain, GLogLevelFlags log_level,
GRegex * regex, GstCheckLogFilterFunc func, gpointer user_data,
GDestroyNotify destroy_data)
{
GstCheckLogFilter *filter;
filter = g_slice_new (GstCheckLogFilter);
filter->log_domain = g_strdup (log_domain);
filter->log_level = log_level;
filter->regex = regex;
filter->func = func;
filter->user_data = user_data;
filter->destroy = destroy_data;
return filter;
}
static void
gst_check_free_log_filter (GstCheckLogFilter * filter)
{
if (!filter)
return;
g_free (filter->log_domain);
g_regex_unref (filter->regex);
if (filter->destroy)
filter->destroy (filter->user_data);
g_slice_free (GstCheckLogFilter, filter);
}
/**
* gst_check_add_log_filter:
* @log_domain: the log domain of the message
* @log_level: the log level of the message
* @regex: (transfer full): a #GRegex to match the message
* @func: the function to call for matching messages
* @user_data: the user data to pass to @func
* @destroy_data: #GDestroyNotify for @user_data
*
* Add a callback @func to be called for all log messages that matches
* @log_domain, @log_level and @regex. If @func is NULL the
* matching logs will be silently discarded by GstCheck.
*
* MT safe.
*
* Returns: A filter that can be passed to @gst_check_remove_log_filter.
*
* Since: 1.12
**/
GstCheckLogFilter *
gst_check_add_log_filter (const gchar * log_domain, GLogLevelFlags log_level,
GRegex * regex, GstCheckLogFilterFunc func, gpointer user_data,
GDestroyNotify destroy_data)
{
GstCheckLogFilter *filter;
g_return_val_if_fail (regex != NULL, NULL);
filter = gst_check_alloc_log_filter (log_domain, log_level, regex,
func, user_data, destroy_data);
g_mutex_lock (&_gst_check_log_filters_mutex);
g_queue_push_tail (&_gst_check_log_filters, filter);
g_mutex_unlock (&_gst_check_log_filters_mutex);
return filter;
}
/**
* gst_check_remove_log_filter:
* @filter: Filter returned by @gst_check_add_log_filter
*
* Remove a filter that has been added by @gst_check_add_log_filter.
*
* MT safe.
*
* Since: 1.12
**/
void
gst_check_remove_log_filter (GstCheckLogFilter * filter)
{
g_mutex_lock (&_gst_check_log_filters_mutex);
g_queue_remove (&_gst_check_log_filters, filter);
gst_check_free_log_filter (filter);
g_mutex_unlock (&_gst_check_log_filters_mutex);
}
/**
* gst_check_clear_log_filter:
*
* Clear all filters added by @gst_check_add_log_filter.
*
* MT safe.
*
* Since: 1.12
**/
void
gst_check_clear_log_filter (void)
{
g_mutex_lock (&_gst_check_log_filters_mutex);
g_queue_foreach (&_gst_check_log_filters,
(GFunc) gst_check_free_log_filter, NULL);
g_queue_clear (&_gst_check_log_filters);
g_mutex_unlock (&_gst_check_log_filters_mutex);
}
typedef struct
{
const gchar * domain;
const gchar * message;
GLogLevelFlags level;
gboolean discard;
} LogFilterApplyData;
static void
gst_check_apply_log_filter (GstCheckLogFilter * filter,
LogFilterApplyData * data)
{
if (gst_check_match_log_filter (filter, data->domain, data->level,
data->message)) {
if (filter->func)
data->discard |= filter->func (data->domain, data->level,
data->message, filter->user_data);
else
data->discard = TRUE;
}
}
static gboolean
gst_check_filter_log_filter (const gchar * log_domain,
GLogLevelFlags log_level, const gchar * message)
{
LogFilterApplyData data;
data.domain = log_domain;
data.level = log_level;
data.message = message;
data.discard = FALSE;
g_mutex_lock (&_gst_check_log_filters_mutex);
g_queue_foreach (&_gst_check_log_filters, (GFunc) gst_check_apply_log_filter,
&data);
g_mutex_unlock (&_gst_check_log_filters_mutex);
if (data.discard)
GST_DEBUG ("Discarding message: %s", message);
return data.discard;
}
static gboolean
gst_check_log_fatal_func (const gchar * log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
if (gst_check_filter_log_filter (log_domain, log_level, message))
return FALSE;
return TRUE;
}
static void gst_check_log_message_func
(const gchar * log_domain, GLogLevelFlags log_level,
const gchar * message, gpointer user_data)
{
if (gst_check_filter_log_filter (log_domain, log_level, message))
return;
if (_gst_check_debug) {
g_print ("%s\n", message);
}
@ -75,6 +268,9 @@ static void gst_check_log_critical_func
(const gchar * log_domain, GLogLevelFlags log_level,
const gchar * message, gpointer user_data)
{
if (gst_check_filter_log_filter (log_domain, log_level, message))
return;
if (!_gst_check_expecting_log) {
g_print ("\n\nUnexpected critical/warning: %s\n", message);
fail ("Unexpected critical/warning: %s", message);
@ -119,6 +315,13 @@ print_plugins (void)
gst_plugin_list_free (plugins);
}
static void
gst_check_deinit (void)
{
gst_deinit ();
gst_check_clear_log_filter ();
}
/* gst_check_init:
* @argc: (inout) (allow-none): pointer to application's argc
* @argv: (inout) (array length=argc) (allow-none): pointer to application's argv
@ -155,8 +358,8 @@ gst_check_init (int *argc, char **argv[])
GST_DEBUG_CATEGORY_INIT (check_debug, "check", 0, "check regression tests");
if (atexit (gst_deinit) != 0) {
GST_ERROR ("failed to set gst_deinit as exit function");
if (atexit (gst_check_deinit) != 0) {
GST_ERROR ("failed to set gst_check_deinit as exit function");
}
if (g_getenv ("GST_TEST_DEBUG"))
@ -174,6 +377,7 @@ gst_check_init (int *argc, char **argv[])
gst_check_log_critical_func, NULL);
g_log_set_handler ("GLib", G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING,
gst_check_log_critical_func, NULL);
g_test_log_set_fatal_handler (gst_check_log_fatal_func, NULL);
print_plugins ();

View file

@ -63,8 +63,33 @@ typedef struct
}
GstCheckABIStruct;
typedef struct _GstCheckLogFilter GstCheckLogFilter;
/**
* GstCheckLogFilterFunc:
* @log_domain: the log domain of the message
* @log_level: the log level of the message
* @message: the message that has occured
* @user_data: user data
*
* A function that is called for messages matching the filter added by
* @gst_check_add_log_filter.
*
* Returns: %TRUE if message should be discarded by GstCheck.
*
* Since: 1.12
*/
typedef gboolean (*GstCheckLogFilterFunc) (const gchar * log_domain,
GLogLevelFlags log_level, const gchar * message, gpointer user_data);
void gst_check_init (int *argc, char **argv[]);
GstCheckLogFilter * gst_check_add_log_filter (const gchar * log,
GLogLevelFlags log_level, GRegex * regex, GstCheckLogFilterFunc func,
gpointer user_data, GDestroyNotify destroy_data);
void gst_check_remove_log_filter (GstCheckLogFilter * filter);
void gst_check_clear_log_filter (void);
GstFlowReturn gst_check_chain_func (GstPad * pad, GstObject * parent, GstBuffer * buffer);
void gst_check_message_error (GstMessage * message, GstMessageType type,