validate: Turn GstValidateRunner into a GstTracer

This way we do not need the LD_PRELOAD hack anymore

Add a new libgstvalidateplugin GStreamer plugin, making sure it shares
the exact same code as the library (exposing only the wanted symbols).

Fix the way we set where to install GstValidate plugins

Try to keep backward compatibility even if tracers should never be instantiated
after an GstElement has been instantiated.

Differential Revision: https://phabricator.freedesktop.org/D459
This commit is contained in:
Thibault Saunier 2015-10-24 09:28:51 +02:00 committed by Thibault Saunier
parent 1d9c2d2a0b
commit 8c760b0a0e
20 changed files with 437 additions and 410 deletions

View file

@ -214,14 +214,17 @@ AC_DEFINE_UNQUOTED(GST_LICENSE, "$GST_LICENSE", [GStreamer license])
AC_SUBST(GST_LICENSE)
dnl define location of plugin directory
AS_AC_EXPAND(PLUGINDIR, ${libdir}/gstreamer-$GST_API_VERSION/validate)
AC_DEFINE_UNQUOTED(PLUGINDIR, "$PLUGINDIR",
AS_AC_EXPAND(VALIDATEPLUGINDIR, ${libdir}/gstreamer-$GST_API_VERSION/validate)
AC_DEFINE_UNQUOTED(VALIDATEPLUGINDIR, "$VALIDATEPLUGINDIR",
[directory where GstValidate plugins are located])
AC_MSG_NOTICE([Using $PLUGINDIR as the plugin install location for GstValidate])
AC_MSG_NOTICE([Using $VALIDATEPLUGINDIR as the plugin install location for GstValidate])
dnl plugin directory configure-time variable for use in Makefile.am
plugindir="\$(libdir)/gstreamer-$GST_API_VERSION/validate"
AC_SUBST(plugindir)
validateplugindir="\$(libdir)/gstreamer-$GST_API_VERSION/validate"
AC_SUBST(validateplugindir)
dnl set location of plugin directory
AG_GST_SET_PLUGINDIR
# set by AG_GST_PARSE_SUBSYSTEM_DISABLES above
dnl make sure it doesn't complain about unused variables if debugging is disabled
@ -317,7 +320,6 @@ data/Makefile
data/scenarios/Makefile
gst/Makefile
gst/validate/Makefile
gst/preload/Makefile
gst/overrides/Makefile
plugins/Makefile
plugins/fault_injection/Makefile

View file

@ -1,5 +1 @@
SUBDIRS = validate overrides
if HAVE_LD_PRELOAD
SUBDIRS += preload
endif

View file

@ -1,15 +0,0 @@
libgstvalidate_preload_@GST_API_VERSION@_la_SOURCES = \
gst-validate-monitor-preload.c
libgstvalidate_preload_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS)
libgstvalidate_preload_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \
$(GST_LT_LDFLAGS) $(top_builddir)/gst/validate/libgstvalidate-1.0.la
libgstvalidate_preload_@GST_API_VERSION@_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
$(GST_ALL_LIBS)
libgstvalidate_preload_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/validate
libgstvalidate_preload_@GST_API_VERSION@include_HEADERS =
lib_LTLIBRARIES = libgstvalidate_preload-@GST_API_VERSION@.la
CLEANFILES =

View file

@ -1,164 +0,0 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
*
* gst-validate-monitor-preload.c - Validate Element monitors preload functions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <gst/gst.h>
#include <string.h>
#include <stdlib.h>
#include <gst/validate/validate.h>
#define __USE_GNU
#include <dlfcn.h>
static GstValidateRunner *runner = NULL;
static void
exit_report_printer (void)
{
if (runner)
gst_validate_runner_exit (runner, TRUE);
}
/*
* Functions that wrap object creation so gst-validate can be used
* to monitor 'standard' applications
*/
static void
gst_validate_preload_wrap (GstElement * element)
{
if (runner == NULL) {
gst_validate_init ();
runner = gst_validate_runner_new ();
atexit (exit_report_printer);
}
/* the reference to the monitor is lost */
gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), runner, NULL);
}
GstElement *
gst_element_factory_make (const gchar * element_name, const gchar * name)
{
static GstElement *(*gst_element_factory_make_real) (const gchar *,
const gchar *) = NULL;
GstElement *element;
if (!gst_element_factory_make_real)
gst_element_factory_make_real =
dlsym (RTLD_NEXT, "gst_element_factory_make");
element = gst_element_factory_make_real (element_name, name);
if (GST_IS_PIPELINE (element)) {
gst_validate_preload_wrap (element);
}
return element;
}
GstElement *
gst_pipeline_new (const gchar * name)
{
static GstElement *(*gst_pipeline_new_real) (const gchar *) = NULL;
GstElement *element;
if (!gst_pipeline_new_real)
gst_pipeline_new_real = dlsym (RTLD_NEXT, "gst_pipeline_new");
element = gst_pipeline_new_real (name);
gst_validate_preload_wrap (element);
return element;
}
GstElement *
gst_parse_launchv (const gchar ** argv, GError ** error)
{
static GstElement *(*gst_parse_launchv_real) (const gchar **, GError **) =
NULL;
GstElement *element;
if (!gst_parse_launchv_real)
gst_parse_launchv_real = dlsym (RTLD_NEXT, "gst_parse_launchv");
element = gst_parse_launchv_real (argv, error);
if (GST_IS_PIPELINE (element)) {
gst_validate_preload_wrap (element);
}
return element;
}
GstElement *
gst_parse_launchv_full (const gchar ** argv, GstParseContext * context,
GstParseFlags flags, GError ** error)
{
static GstElement *(*gst_parse_launchv_full_real) (const gchar **,
GstParseContext *, GstParseFlags, GError **) = NULL;
GstElement *element;
if (!gst_parse_launchv_full_real)
gst_parse_launchv_full_real = dlsym (RTLD_NEXT, "gst_parse_launchv_full");
element = gst_parse_launchv_full_real (argv, context, flags, error);
if (GST_IS_PIPELINE (element)) {
gst_validate_preload_wrap (element);
}
return element;
}
GstElement *
gst_parse_launch (const gchar * pipeline_description, GError ** error)
{
static GstElement *(*gst_parse_launch_real) (const gchar *, GError **) = NULL;
GstElement *element;
if (!gst_parse_launch_real)
gst_parse_launch_real = dlsym (RTLD_NEXT, "gst_parse_launch");
element = gst_parse_launch_real (pipeline_description, error);
if (GST_IS_PIPELINE (element)) {
gst_validate_preload_wrap (element);
}
return element;
}
GstElement *
gst_parse_launch_full (const gchar * pipeline_description,
GstParseContext * context, GstParseFlags flags, GError ** error)
{
static GstElement *(*gst_parse_launch_full_real) (const gchar *,
GstParseContext *, GstParseFlags, GError **) = NULL;
GstElement *element;
if (!gst_parse_launch_full_real)
gst_parse_launch_full_real = dlsym (RTLD_NEXT, "gst_parse_launch_full");
element =
gst_parse_launch_full_real (pipeline_description, context, flags, error);
if (GST_IS_PIPELINE (element)) {
gst_validate_preload_wrap (element);
}
return element;
}

View file

@ -1,4 +1,4 @@
libgstvalidate_@GST_API_VERSION@_la_SOURCES = \
source_c = \
gst-validate-runner.c \
gst-validate-reporter.c \
gst-validate-monitor.c \
@ -18,7 +18,7 @@ libgstvalidate_@GST_API_VERSION@_la_SOURCES = \
gst-validate-media-info.c \
validate.c
libgstvalidate_@GST_API_VERSION@include_HEADERS = \
source_h = \
validate.h \
gst-validate-types.h \
gst-validate-bin-monitor.h \
@ -45,9 +45,13 @@ noinst_HEADERS = \
gst-validate-i18n-lib.h \
gst-validate-internal.h
# GstValidate library
lib_LTLIBRARIES = libgstvalidate-@GST_API_VERSION@.la
libgstvalidate_@GST_API_VERSION@_la_SOURCES = $(source_c)
libgstvalidate_@GST_API_VERSION@include_HEADERS = $(source_h)
libgstvalidate_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS)\
$(GIO_CFLAGS) $(GST_PBUTILS_CFLAGS)
$(GIO_CFLAGS) $(GST_PBUTILS_CFLAGS) \
-DGST_USE_UNSTABLE_API
libgstvalidate_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \
$(GST_LT_LDFLAGS) $(GIO_LDFLAGS) $(GST_PBUTILS_LDFAGS)
libgstvalidate_@GST_API_VERSION@_la_LIBADD = \
@ -57,6 +61,20 @@ libgstvalidate_@GST_API_VERSION@_la_LIBADD = \
libgstvalidate_@GST_API_VERSION@includedir = $(includedir)/gstreamer-@GST_API_VERSION@/gst/validate
# GstValidate GStreamer plugin
plugin_LTLIBRARIES = libgstvalidateplugin-@GST_API_VERSION@.la
libgstvalidateplugin_@GST_API_VERSION@_la_SOURCES = $(source_c)
libgstvalidateplugin_@GST_API_VERSION@_la_CFLAGS = $(GST_ALL_CFLAGS)\
$(GIO_CFLAGS) $(GST_PBUTILS_CFLAGS) \
-DGST_USE_UNSTABLE_API \
-D__GST_VALIDATE_PLUGIN
libgstvalidateplugin_@GST_API_VERSION@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) \
$(GST_LT_LDFLAGS) $(GIO_LDFLAGS) $(GST_PBUTILS_LDFAGS)
libgstvalidateplugin_@GST_API_VERSION@_la_LIBADD = \
$(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) \
$(GST_ALL_LIBS) $(GIO_LIBS) $(GST_PBUTILS_LIBS) \
$(GLIB_LIBS) $(LIBM)
CLEANFILES =
if HAVE_INTROSPECTION

View file

@ -24,6 +24,7 @@
#include <gst/gst.h>
#include "gst-validate-scenario.h"
#include "gst-validate-monitor.h"
GST_DEBUG_CATEGORY_EXTERN (gstvalidate_debug);
#define GST_CAT_DEFAULT gstvalidate_debug
@ -43,6 +44,10 @@ void init_scenarios (void);
* and this is done by the scenario itself now */
GST_EXPORT gboolean _action_check_and_set_printed (GstValidateAction *action);
GST_EXPORT gboolean gst_validate_action_is_subaction (GstValidateAction *action);
void _priv_validate_override_registry_deinit (void);
GST_EXPORT void _priv_validate_override_registry_deinit (void);
G_GNUC_INTERNAL GstValidateMonitor * gst_validate_get_monitor (GObject *object);
G_GNUC_INTERNAL void gst_validate_init_runner (void);
G_GNUC_INTERNAL void gst_validate_deinit_runner (void);
#endif

View file

@ -411,3 +411,9 @@ gst_validate_monitor_set_media_descriptor (GstValidateMonitor * monitor,
if (klass->set_media_descriptor)
klass->set_media_descriptor (monitor, media_descriptor);
}
GstValidateMonitor *
gst_validate_get_monitor (GObject * object)
{
return GST_VALIDATE_MONITOR (g_object_get_data (object, "validate-monitor"));
}

View file

@ -1,7 +1,8 @@
/* GStreamer
*
* Copyright (C) 2013 Collabora Ltd.
* Copyright (C) 2013-2016 Collabora Ltd.
* Author: Thiago Sousa Santos <thiago.sousa.santos@collabora.com>
* Author: Thibault Saunier <thibault.saunier@collabora.com>
*
* gst-validate-runner.c - Validate Runner class
*
@ -28,12 +29,22 @@
# include "config.h"
#endif
#include "validate.h"
#include "gst-validate-internal.h"
#include "gst-validate-report.h"
#include "gst-validate-monitor-factory.h"
#include "gst-validate-override-registry.h"
#include "gst-validate-runner.h"
static gboolean element_created = FALSE;
/* We create a GstValidateRunner on _init ()
* so that we keep backward compatibility when
* the user create a Runner after creating the pipeline
* but the runner was actually already ready to be used.
*/
static GstValidateRunner *first_runner = NULL;
/**
* SECTION:gst-validate-runner
* @short_description: Class that runs Gst Validate tests for a pipeline
@ -68,6 +79,12 @@ struct _GstValidateRunnerPrivate
/* A list of PatternLevel */
GList *report_pattern_levels;
/* Whether the runner was create with GST_TRACERS=validate or not) */
gboolean user_created;
gchar *pipeline_names;
gchar **pipeline_names_strv;
};
/* Describes the reporting level to apply to a name pattern */
@ -92,7 +109,7 @@ typedef struct _PatternLevel
} G_STMT_END
#define gst_validate_runner_parent_class parent_class
G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, G_TYPE_OBJECT);
G_DEFINE_TYPE (GstValidateRunner, gst_validate_runner, GST_TYPE_TRACER);
/* signals */
enum
@ -103,7 +120,62 @@ enum
LAST_SIGNAL
};
static guint _signals[LAST_SIGNAL] = { 0 };
enum
{
PROP_0,
PROP_PARAMS,
PROP_LAST
};
static GParamSpec *properties[PROP_LAST];
static guint _signals[LAST_SIGNAL] = { NULL, };
static gboolean
gst_validate_runner_should_monitor (GstValidateRunner * self,
GstElement * element)
{
gint i;
GstValidateMonitor *monitor;
if (!GST_IS_PIPELINE (element)) {
return FALSE;
}
if (self->priv->user_created)
return FALSE;
if (!self->priv->pipeline_names_strv)
return TRUE;
monitor = gst_validate_get_monitor (G_OBJECT (element));
if (monitor) {
GST_ERROR_OBJECT (self, "Pipeline %" GST_PTR_FORMAT " is already"
" monitored by %" GST_PTR_FORMAT " using runner: %" GST_PTR_FORMAT
" NOT monitoring again.",
element, monitor,
gst_validate_reporter_get_runner (GST_VALIDATE_REPORTER (monitor)));
}
for (i = 0; self->priv->pipeline_names_strv[i]; i++) {
if (g_pattern_match_simple (self->priv->pipeline_names_strv[i],
GST_OBJECT_NAME (element)))
return TRUE;
}
return FALSE;
}
static void
do_element_new (GstValidateRunner * self, guint64 ts, GstElement * element)
{
element_created = TRUE;
if (gst_validate_runner_should_monitor (self, element)) {
/* the reference to the monitor is lost */
gst_validate_monitor_factory_create (GST_OBJECT_CAST (element), self, NULL);
}
}
static gboolean
_parse_reporting_level (gchar * str, GstValidateReportingDetails * level)
@ -233,21 +305,95 @@ _unref_report_list (gpointer unused, GList * reports, gpointer unused_too)
}
static void
gst_validate_runner_dispose (GObject * object)
gst_validate_runner_finalize (GObject * object)
{
GstValidateRunner *runner = GST_VALIDATE_RUNNER_CAST (object);
if (!runner->priv->user_created)
gst_validate_runner_exit (runner, TRUE);
g_list_free_full (runner->priv->reports,
(GDestroyNotify) gst_validate_report_unref);
g_list_free_full (runner->priv->report_pattern_levels,
(GDestroyNotify) _free_report_pattern_level);
g_mutex_clear (&runner->priv->mutex);
g_free (runner->priv->pipeline_names);
g_strfreev (runner->priv->pipeline_names_strv);
g_hash_table_foreach (runner->priv->reports_by_type, (GHFunc)
_unref_report_list, NULL);
g_hash_table_destroy (runner->priv->reports_by_type);
G_OBJECT_CLASS (parent_class)->dispose (object);
G_OBJECT_CLASS (parent_class)->finalize (object);
if (!runner->priv->user_created)
gst_validate_deinit ();
}
static GObject *
gst_validate_runner_constructor (GType type, guint n_construct_params,
GObjectConstructParam * construct_params)
{
GObject *runner = G_OBJECT_CLASS (parent_class)->constructor (type,
n_construct_params, construct_params);
if (!gst_validate_is_initialized ()) {
first_runner = GST_VALIDATE_RUNNER (runner);
gst_validate_init ();
first_runner = NULL;
return runner;
}
return runner;
}
static void
gst_validate_runner_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstValidateRunner *runner;
runner = GST_VALIDATE_RUNNER (object);
switch (prop_id) {
case PROP_PARAMS:
{
g_value_set_string (value, runner->priv->pipeline_names);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_validate_runner_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstValidateRunner *runner;
runner = GST_VALIDATE_RUNNER (object);
switch (prop_id) {
case PROP_PARAMS:
{
g_free (runner->priv->pipeline_names);
g_strfreev (runner->priv->pipeline_names_strv);
runner->priv->pipeline_names = g_value_dup_string (value);
if (runner->priv->pipeline_names)
runner->priv->pipeline_names_strv =
g_strsplit (runner->priv->pipeline_names, ",", -1);
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
@ -257,10 +403,20 @@ gst_validate_runner_class_init (GstValidateRunnerClass * klass)
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = gst_validate_runner_dispose;
gobject_class->finalize = gst_validate_runner_finalize;
gobject_class->set_property = gst_validate_runner_set_property;
gobject_class->get_property = gst_validate_runner_get_property;
gobject_class->constructor = gst_validate_runner_constructor;
g_type_class_add_private (klass, sizeof (GstValidateRunnerPrivate));
properties[PROP_PARAMS] =
g_param_spec_string ("params", "Params", "Extra configuration parameters",
NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (gobject_class, PROP_LAST, properties);
_signals[REPORT_ADDED_SIGNAL] =
g_signal_new ("report-added", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1,
@ -283,6 +439,9 @@ gst_validate_runner_init (GstValidateRunner * runner)
runner->priv->default_level = GST_VALIDATE_SHOW_DEFAULT;
_init_report_levels (runner);
gst_tracing_register_hook (GST_TRACER (runner), "element-new",
G_CALLBACK (do_element_new));
}
/**
@ -295,7 +454,22 @@ gst_validate_runner_init (GstValidateRunner * runner)
GstValidateRunner *
gst_validate_runner_new (void)
{
return g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
GstValidateRunner *runner;
if (first_runner) {
runner = first_runner;
first_runner = NULL;
} else if (element_created) {
g_error ("Should never create a GstValidateRunner after a GstElement"
"has been created in the same process.");
return NULL;
} else {
runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
runner->priv->user_created = TRUE;
}
return runner;
}
/*
@ -537,3 +711,33 @@ gst_validate_runner_exit (GstValidateRunner * runner, gboolean print_result)
return ret;
}
void
gst_validate_init_runner (void)
{
if (!first_runner) {
first_runner = g_object_new (GST_TYPE_VALIDATE_RUNNER, NULL);
first_runner->priv->user_created = TRUE;
} /* else the first runner has been created through the GST_TRACERS system */
}
void
gst_validate_deinit_runner (void)
{
g_clear_object (&first_runner);
}
#ifdef __GST_VALIDATE_PLUGIN
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_tracer_register (plugin, "validate", GST_TYPE_VALIDATE_RUNNER))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, validatetracer,
"GStreamer Validate tracers", plugin_init, VERSION, GST_LICENSE,
GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
#endif /* __GST_VALIDATE_PLUGIN */

View file

@ -25,6 +25,8 @@
#include <glib-object.h>
#include <gst/gst.h>
#include <gst/gsttracer.h>
typedef struct _GstValidateRunner GstValidateRunner;
typedef struct _GstValidateRunnerClass GstValidateRunnerClass;
@ -52,7 +54,7 @@ typedef struct _GstValidateRunnerPrivate GstValidateRunnerPrivate;
* Class that manages a Validate test run for some pipeline
*/
struct _GstValidateRunner {
GObject object;
GstTracer object;
/* <private> */
GstValidateRunnerPrivate *priv;
@ -65,7 +67,7 @@ struct _GstValidateRunner {
* GStreamer Validate Runner object class.
*/
struct _GstValidateRunnerClass {
GObjectClass parent_class;
GstTracerClass parent_class;
};
/* normal GObject stuff */

View file

@ -261,6 +261,7 @@ gst_validate_init (void)
validate_initialized = TRUE;
gst_validate_init_plugins ();
gst_validate_init_runner ();
}
void
@ -268,8 +269,9 @@ gst_validate_deinit (void)
{
g_mutex_lock (&_gst_validate_registry_mutex);
_free_plugin_config (core_config);
gst_object_unref (_gst_validate_registry_default);
_gst_validate_registry_default = NULL;
gst_validate_deinit_runner ();
g_clear_object (&_gst_validate_registry_default);
_priv_validate_override_registry_deinit ();
core_config = NULL;

View file

@ -1,4 +1,4 @@
plugin_LTLIBRARIES = libgstvalidatefaultinjection.la
validateplugin_LTLIBRARIES = libgstvalidatefaultinjection.la
libgstvalidatefaultinjection_la_SOURCES = \
socket_interposer.c

View file

@ -1,4 +1,4 @@
plugin_LTLIBRARIES = libgstvalidategapplication.la
validateplugin_LTLIBRARIES = libgstvalidategapplication.la
libgstvalidategapplication_la_SOURCES = \
gstvalidategapplication.c

View file

@ -1,4 +1,4 @@
plugin_LTLIBRARIES = libgstvalidategtk.la
validateplugin_LTLIBRARIES = libgstvalidategtk.la
libgstvalidategtk_la_SOURCES = gstvalidategtk.c

View file

@ -1,4 +1,4 @@
plugin_LTLIBRARIES = libgstvalidatessim.la
validateplugin_LTLIBRARIES = libgstvalidatessim.la
libgstvalidatessim_la_SOURCES = gstvalidatessim.c

View file

@ -85,8 +85,8 @@ GST_START_TEST (monitors_cleanup)
g_object_get_data ((GObject *) src->srcpads->data, "validate-monitor");
pmonitor2 =
g_object_get_data ((GObject *) sink->sinkpads->data, "validate-monitor");
check_destroyed (monitor, pmonitor1, pmonitor2, NULL);
check_destroyed (pipeline, src, sink, NULL);
gst_check_objects_destroyed_on_unref (monitor, pmonitor1, pmonitor2, NULL);
gst_check_objects_destroyed_on_unref (pipeline, src, sink, NULL);
}
GST_END_TEST;
@ -99,12 +99,9 @@ gst_validate_suite (void)
TCase *tc_chain = tcase_create ("monitoring");
suite_add_tcase (s, tc_chain);
gst_validate_init ();
tcase_add_test (tc_chain, monitors_added);
tcase_add_test (tc_chain, monitors_cleanup);
gst_validate_deinit ();
return s;
}

View file

@ -27,17 +27,16 @@ static const gchar *some_overrides =
"change-severity, issue-id=buffer::not-expected-one, new-severity=warning, element-factory-name=queue";
static void
_check_message_level (const gchar * factoryname, GstValidateReportLevel level,
const gchar * message_id)
_check_message_level (GstValidateRunner * runner,
gint previous_reports, const gchar * factoryname,
GstValidateReportLevel level, const gchar * message_id)
{
GList *reports;
GstElement *element;
GstValidateRunner *runner;
GstValidateMonitor *monitor;
element = gst_element_factory_make (factoryname, NULL);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE));
runner = gst_validate_runner_new ();
monitor =
gst_validate_monitor_factory_create (GST_OBJECT (element), runner, NULL);
@ -45,8 +44,9 @@ _check_message_level (const gchar * factoryname, GstValidateReportLevel level,
"Just some fakery");
reports = gst_validate_runner_get_reports (runner);
fail_unless_equals_int (g_list_length (reports), 1);
fail_unless_equals_int (((GstValidateReport *) reports->data)->level, level);
fail_unless_equals_int (g_list_length (reports), previous_reports + 1);
fail_unless_equals_int (((GstValidateReport *) g_list_nth_data (reports,
previous_reports))->level, level);
g_list_free_full (reports, (GDestroyNotify) gst_validate_report_unref);
gst_object_unref (element);
gst_object_unref (monitor);
@ -56,6 +56,7 @@ _check_message_level (const gchar * factoryname, GstValidateReportLevel level,
GST_START_TEST (check_text_overrides)
{
GstValidateIssue *issue;
GstValidateRunner *runner = gst_validate_runner_new ();
gchar *override_filename =
g_strdup_printf ("%s%c%s", g_get_tmp_dir (), G_DIR_SEPARATOR,
"some_overrides");
@ -76,13 +77,13 @@ GST_START_TEST (check_text_overrides)
/* Check that with a queue, the level of a
* buffer::not-expected-one is WARNING */
_check_message_level ("queue", GST_VALIDATE_REPORT_LEVEL_WARNING,
_check_message_level (runner, 0, "queue", GST_VALIDATE_REPORT_LEVEL_WARNING,
"buffer::not-expected-one");
/* Check that with an identity, the level of a
* buffer::not-expected-one is CRITICAL */
_check_message_level ("identity", GST_VALIDATE_REPORT_LEVEL_CRITICAL,
"buffer::not-expected-one");
_check_message_level (runner, 1, "identity",
GST_VALIDATE_REPORT_LEVEL_CRITICAL, "buffer::not-expected-one");
g_remove (override_filename);
g_free (override_filename);
@ -98,11 +99,11 @@ gst_validate_suite (void)
TCase *tc_chain = tcase_create ("registry");
suite_add_tcase (s, tc_chain);
g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE);
gst_validate_init ();
tcase_add_test (tc_chain, check_text_overrides);
gst_validate_deinit ();
return s;
}

View file

@ -50,7 +50,7 @@ _stop_monitoring_bin (GstBin * bin, GstValidateRunner * runner)
gst_object_unref (bin);
ASSERT_OBJECT_REFCOUNT (monitor, "monitor", 1);
gst_object_unref (monitor);
ASSERT_OBJECT_REFCOUNT (runner, "runner", 1);
ASSERT_OBJECT_REFCOUNT (runner, "runner", 2);
gst_object_unref (runner);
}
@ -144,9 +144,10 @@ GST_START_TEST (buffer_before_segment)
_check_reports_refcount (srcpad, 2);
gst_object_unref (srcpad);
check_destroyed (src, srcpad, NULL);
check_destroyed (sink, NULL, NULL);
check_destroyed (runner, NULL, NULL);
gst_check_objects_destroyed_on_unref (src, srcpad, NULL);
gst_check_object_destroyed_on_unref (sink);
ASSERT_OBJECT_REFCOUNT (runner, "runner", 2);
gst_object_unref (runner);
}
GST_END_TEST;
@ -338,34 +339,26 @@ _test_flow_aggregation (GstFlowReturn flow, GstFlowReturn flow1,
gst_object_unref (demuxer);
ASSERT_OBJECT_REFCOUNT (pmonitor, "plop", 1);
gst_object_unref (pmonitor);
}
GST_START_TEST (flow_aggregation)
{
/* Check the GstFlowCombiner to find the rules */
#define FLOW_TEST(name, flow1, flow2, flow3, demux_flow, fails) \
GST_START_TEST(flow_aggregation_##name) { \
_test_flow_aggregation (GST_FLOW_##flow1, GST_FLOW_##flow2, \
GST_FLOW_##flow3, GST_FLOW_##demux_flow, fails); \
} GST_END_TEST
/* Failling cases: */
_test_flow_aggregation (GST_FLOW_OK, GST_FLOW_OK,
GST_FLOW_ERROR, GST_FLOW_OK, TRUE);
_test_flow_aggregation (GST_FLOW_EOS, GST_FLOW_EOS,
GST_FLOW_EOS, GST_FLOW_OK, TRUE);
_test_flow_aggregation (GST_FLOW_FLUSHING, GST_FLOW_OK,
GST_FLOW_OK, GST_FLOW_OK, TRUE);
_test_flow_aggregation (GST_FLOW_NOT_NEGOTIATED, GST_FLOW_OK,
GST_FLOW_OK, GST_FLOW_OK, TRUE);
/* Passing cases: */
_test_flow_aggregation (GST_FLOW_EOS, GST_FLOW_EOS,
GST_FLOW_EOS, GST_FLOW_EOS, FALSE);
_test_flow_aggregation (GST_FLOW_EOS, GST_FLOW_EOS,
GST_FLOW_OK, GST_FLOW_OK, FALSE);
_test_flow_aggregation (GST_FLOW_OK, GST_FLOW_OK,
GST_FLOW_OK, GST_FLOW_EOS, FALSE);
_test_flow_aggregation (GST_FLOW_NOT_NEGOTIATED, GST_FLOW_OK,
GST_FLOW_OK, GST_FLOW_NOT_NEGOTIATED, FALSE);
}
GST_END_TEST;
FLOW_TEST (ok_ok_error_ok, OK, OK, ERROR, OK, TRUE);
FLOW_TEST (eos_eos_eos_ok, EOS, EOS, EOS, OK, TRUE);
FLOW_TEST (flushing_ok_ok_ok, FLUSHING, OK, OK, OK, TRUE);
FLOW_TEST (not_neg_ok_ok_ok, NOT_NEGOTIATED, OK, OK, OK, TRUE);
/*[> Passing cases: <]*/
FLOW_TEST (eos_eos_eos_eos, EOS, EOS, EOS, EOS, FALSE);
FLOW_TEST (eos_eos_ok_ok, EOS, EOS, OK, OK, FALSE);
FLOW_TEST (ok_ok_ok_eos, OK, OK, OK, EOS, FALSE);
FLOW_TEST (not_neg_ok_ok_not_neg, NOT_NEGOTIATED, OK, OK, NOT_NEGOTIATED,
FALSE);
#undef FLOW_TEST
GST_START_TEST (issue_concatenation)
{
@ -492,11 +485,13 @@ GST_START_TEST (issue_concatenation)
gst_object_unref (sinkpad);
gst_object_unref (fakemixer_sink1);
gst_object_unref (fakemixer_sink2);
check_destroyed (fakemixer, fakemixer_sink1, fakemixer_sink2, NULL);
check_destroyed (src1, srcpad1, NULL);
check_destroyed (src2, srcpad2, NULL);
check_destroyed (sink, sinkpad, NULL);
check_destroyed (runner, NULL, NULL);
gst_check_objects_destroyed_on_unref (fakemixer, fakemixer_sink1,
fakemixer_sink2, NULL);
gst_check_objects_destroyed_on_unref (src1, srcpad1, NULL);
gst_check_objects_destroyed_on_unref (src2, srcpad2, NULL);
gst_check_objects_destroyed_on_unref (sink, sinkpad, NULL);
ASSERT_OBJECT_REFCOUNT (runner, "runner", 2);
gst_object_unref (runner);
}
GST_END_TEST;
@ -629,18 +624,25 @@ _check_media_info (GstSegment * segment, BufferDesc * bufs)
GST_STATE_CHANGE_SUCCESS);
gst_object_unref (srcpad);
check_destroyed (decoder, sinkpad, NULL);
check_destroyed (runner, NULL, NULL);
gst_check_objects_destroyed_on_unref (decoder, sinkpad, NULL);
ASSERT_OBJECT_REFCOUNT (runner, "runner", 2);
gst_object_unref (runner);
}
GST_START_TEST (check_media_info)
{
GstSegment segment;
#define MEDIA_INFO_TEST(name,segment_start,bufs) \
GST_START_TEST(media_info_##name) { \
if (segment_start >= 0) { \
GstSegment segment; \
gst_segment_init (&segment, GST_FORMAT_TIME); \
segment.start = segment_start; \
_check_media_info (&segment, (bufs)); \
} else \
_check_media_info (NULL, (bufs)); \
} GST_END_TEST
/* *INDENT-OFF* */
_check_media_info (NULL,
(BufferDesc []) {
MEDIA_INFO_TEST (1, -1,
((BufferDesc []) {
{
.content = "buffer1",
.pts = 0,
@ -674,16 +676,11 @@ GST_START_TEST (check_media_info)
.num_issues = 1
},
{ NULL}
});
/* *INDENT-ON* */
}));
gst_segment_init (&segment, GST_FORMAT_TIME);
/* Segment start is 2, the first buffer is expected (first Keyframe) */
segment.start = 2;
/* *INDENT-OFF* */
_check_media_info (&segment,
(BufferDesc []) {
/* Segment start is 2, the first buffer is expected (first Keyframe) */
MEDIA_INFO_TEST (2, 2,
((BufferDesc []) {
{
.content = "buffer2", /* Wrong checksum */
.pts = 0,
@ -693,16 +690,11 @@ GST_START_TEST (check_media_info)
.num_issues = 1
},
{ NULL}
});
/* *INDENT-ON* */
}));
gst_segment_init (&segment, GST_FORMAT_TIME);
/* Segment start is 2, the first buffer is expected (first Keyframe) */
segment.start = 2;
/* *INDENT-OFF* */
_check_media_info (&segment,
(BufferDesc []) {
/* Segment start is 2, the first buffer is expected (first Keyframe) */
MEDIA_INFO_TEST (3, 2,
((BufferDesc []) {
{ /* The right first buffer */
.content = "buffer1",
.pts = 0,
@ -712,16 +704,11 @@ GST_START_TEST (check_media_info)
.num_issues = 0
},
{ NULL}
});
/* *INDENT-ON* */
}));
gst_segment_init (&segment, GST_FORMAT_TIME);
/* Segment start is 6, the 4th buffer is expected (first Keyframe) */
segment.start = 6;
/* *INDENT-OFF* */
_check_media_info (&segment,
(BufferDesc []) {
/* Segment start is 6, the 4th buffer is expected (first Keyframe) */
MEDIA_INFO_TEST (4, 6,
((BufferDesc []) {
{ /* The right fourth buffer */
.content = "buffer4",
.pts = 4,
@ -731,16 +718,11 @@ GST_START_TEST (check_media_info)
.num_issues = 0
},
{ NULL}
});
/* *INDENT-ON* */
}));
gst_segment_init (&segment, GST_FORMAT_TIME);
/* Segment start is 6, the 4th buffer is expected (first Keyframe) */
segment.start = 6;
/* *INDENT-OFF* */
_check_media_info (&segment,
(BufferDesc []) {
/* Segment start is 6, the 4th buffer is expected (first Keyframe) */
MEDIA_INFO_TEST (5, 6,
((BufferDesc []) {
{ /* The sixth buffer... all wrong! */
.content = "buffer6",
.pts = 6,
@ -750,11 +732,8 @@ GST_START_TEST (check_media_info)
.num_issues = 1
},
{ NULL}
});
}));
/* *INDENT-ON* */
}
GST_END_TEST;
GST_START_TEST (caps_events)
{
@ -1046,21 +1025,33 @@ gst_validate_suite (void)
TCase *tc_chain = tcase_create ("padmonitor");
suite_add_tcase (s, tc_chain);
gst_validate_init ();
fake_elements_register ();
tcase_add_test (tc_chain, buffer_before_segment);
tcase_add_test (tc_chain, buffer_outside_segment);
tcase_add_test (tc_chain, buffer_timestamp_out_of_received_range);
tcase_add_test (tc_chain, flow_aggregation);
tcase_add_test (tc_chain, media_info_1);
tcase_add_test (tc_chain, media_info_2);
tcase_add_test (tc_chain, media_info_3);
tcase_add_test (tc_chain, media_info_4);
tcase_add_test (tc_chain, media_info_5);
tcase_add_test (tc_chain, flow_aggregation_ok_ok_error_ok);
tcase_add_test (tc_chain, flow_aggregation_eos_eos_eos_ok);
tcase_add_test (tc_chain, flow_aggregation_flushing_ok_ok_ok);
tcase_add_test (tc_chain, flow_aggregation_not_neg_ok_ok_ok);
tcase_add_test (tc_chain, flow_aggregation_eos_eos_eos_eos);
tcase_add_test (tc_chain, flow_aggregation_eos_eos_ok_ok);
tcase_add_test (tc_chain, flow_aggregation_ok_ok_ok_eos);
tcase_add_test (tc_chain, flow_aggregation_not_neg_ok_ok_not_neg);
tcase_add_test (tc_chain, issue_concatenation);
tcase_add_test (tc_chain, check_media_info);
tcase_add_test (tc_chain, eos_without_segment);
tcase_add_test (tc_chain, caps_events);
tcase_add_test (tc_chain, flow_error_without_message);
tcase_add_test (tc_chain, flow_error_with_message);
gst_validate_deinit ();
return s;
}

View file

@ -21,14 +21,9 @@
#include <gst/check/gstcheck.h>
#include "test-utils.h"
GST_START_TEST (test_report_levels)
GST_START_TEST (test_report_levels_all)
{
GstValidateRunner *runner;
GstObject *pipeline;
GError *error = NULL;
GstElement *element;
GstValidateMonitor *monitor, *pipeline_monitor;
GstPad *pad;
/* FIXME: for now the only interface to set the reporting level is through an
* environment variable parsed at the time of the runner initialization,
@ -42,6 +37,14 @@ GST_START_TEST (test_report_levels)
fail_unless (gst_validate_runner_get_default_reporting_level (runner) ==
GST_VALIDATE_SHOW_ALL);
g_object_unref (runner);
}
GST_END_TEST;
GST_START_TEST (test_report_levels_2)
{
GstValidateRunner *runner;
/* Try to set the default reporting level to subchain, the code is supposed to
* parse numbers as well */
@ -50,6 +53,13 @@ GST_START_TEST (test_report_levels)
fail_unless (gst_validate_runner_get_default_reporting_level (runner) ==
GST_VALIDATE_SHOW_SYNTHETIC);
g_object_unref (runner);
}
GST_END_TEST;
GST_START_TEST (test_report_levels_complex_parsing)
{
GstValidateRunner *runner;
/* Try to set the reporting level for an object */
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS",
@ -63,6 +73,18 @@ GST_START_TEST (test_report_levels)
"dummy_test_object") == GST_VALIDATE_SHOW_UNKNOWN);
g_object_unref (runner);
}
GST_END_TEST;
GST_START_TEST (test_complex_reporting_details)
{
GstPad *pad;
GstObject *pipeline;
GstElement *element;
GError *error = NULL;
GstValidateMonitor *monitor, *pipeline_monitor;
GstValidateRunner *runner;
/* Now let's try to see if the created monitors actually understand the
* situation they've put themselves into */
@ -190,92 +212,40 @@ _create_issues (GstValidateRunner * runner)
check_destroyed (sink, sinkpad, NULL);
}
GST_START_TEST (test_global_levels)
{
GstValidateRunner *runner;
#define TEST_LEVELS(name, details, num_issues) \
GST_START_TEST (test_global_level_##name) { \
GstValidateRunner *runner; \
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", details, TRUE)); \
runner = gst_validate_runner_new (); \
_create_issues (runner); \
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), num_issues); \
g_object_unref (runner); \
} GST_END_TEST
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "none", TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* None shall pass */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 0);
g_object_unref (runner);
TEST_LEVELS (none, "none", 0);
TEST_LEVELS (synthetic, "synthetic", 1);
TEST_LEVELS (monitor, "monitor", 6);
TEST_LEVELS (all, "all", 8);
TEST_LEVELS (none_fakesink_synthetic, "none,fakesrc1:synthetic", 1);
/* 5 issues because all pads will report their own issues separately, except
* for the sink which will not report an issue */
TEST_LEVELS (monitor_sink_none, "monitor,sink:none", 5);
/* 3 issues because both fake sources will have subsequent subchains of
* issues, and the sink will report its issue separately */
TEST_LEVELS (subchain_sink_monitor, "subchain,sink:monitor", 3);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "synthetic", TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* Two reports of the same type */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 1);
g_object_unref (runner);
/* 4 issues because the fakemixer sink issues will be concatenated with the
* fakesrc issues, the fakemixer src will report its issue separately, and the
* sink will not find a report immediately upstream */
TEST_LEVELS
(synthetic_fakesrc1_subchain_fakesrc2_subchain_fakemixer_src_monitor,
"synthetic,fakesrc1:subchain,fakesrc2:subchain,fakemixer*::src*:monitor",
4);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "monitor", TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* One report for each pad monitor */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 6);
g_object_unref (runner);
/* 2 issues repeated on the fakesink's sink */
TEST_LEVELS (none_fakesink_all, "none,fakesink*:all", 2);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "all", TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* One report for each pad monitor, plus one for fakemixer src and fakesink sink */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 8);
g_object_unref (runner);
}
GST_END_TEST;
GST_START_TEST (test_specific_levels)
{
GstValidateRunner *runner;
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS",
"none,fakesrc1:synthetic", TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* One issue should go through the none filter */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 1);
g_object_unref (runner);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "monitor,sink:none",
TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* 5 issues because all pads will report their own issues separately, except
* for the sink which will not report an issue */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 5);
g_object_unref (runner);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS",
"subchain,sink:monitor", TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* 3 issues because both fake sources will have subsequent subchains of
* issues, and the sink will report its issue separately */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 3);
g_object_unref (runner);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS",
"synthetic,fakesrc1:subchain,fakesrc2:subchain,fakemixer*::src*:monitor",
TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* 4 issues because the fakemixer sink issues will be concatenated with the
* fakesrc issues, the fakemixer src will report its issue separately, and the
* sink will not find a report immediately upstream */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 4);
g_object_unref (runner);
fail_unless (g_setenv ("GST_VALIDATE_REPORTING_DETAILS", "none,fakesink*:all",
TRUE));
runner = gst_validate_runner_new ();
_create_issues (runner);
/* 2 issues repeated on the fakesink's sink */
fail_unless_equals_int (gst_validate_runner_get_reports_count (runner), 2);
g_object_unref (runner);
}
GST_END_TEST;
#undef TEST_LEVELS
static Suite *
gst_validate_suite (void)
@ -284,14 +254,24 @@ gst_validate_suite (void)
TCase *tc_chain = tcase_create ("reporting");
suite_add_tcase (s, tc_chain);
gst_validate_init ();
fake_elements_register ();
tcase_add_test (tc_chain, test_report_levels);
tcase_add_test (tc_chain, test_global_levels);
tcase_add_test (tc_chain, test_specific_levels);
tcase_add_test (tc_chain, test_report_levels_all);
tcase_add_test (tc_chain, test_report_levels_2);
tcase_add_test (tc_chain, test_report_levels_complex_parsing);
tcase_add_test (tc_chain, test_complex_reporting_details);
tcase_add_test (tc_chain, test_global_level_none);
tcase_add_test (tc_chain, test_global_level_synthetic);
tcase_add_test (tc_chain, test_global_level_monitor);
tcase_add_test (tc_chain, test_global_level_all);
tcase_add_test (tc_chain, test_global_level_none_fakesink_synthetic);
tcase_add_test (tc_chain, test_global_level_monitor_sink_none);
tcase_add_test (tc_chain, test_global_level_subchain_sink_monitor);
tcase_add_test (tc_chain,
test_global_level_synthetic_fakesrc1_subchain_fakesrc2_subchain_fakemixer_src_monitor);
tcase_add_test (tc_chain, test_global_level_none_fakesink_all);
gst_validate_deinit ();
return s;
}

View file

@ -925,6 +925,7 @@ main (int argc, gchar ** argv)
}
/* Create the pipeline */
runner = gst_validate_runner_new ();
create_transcoding_pipeline (argv[1], argv[2]);
#ifdef G_OS_UNIX
@ -932,7 +933,6 @@ main (int argc, gchar ** argv)
g_unix_signal_add (SIGINT, (GSourceFunc) intr_handler, pipeline);
#endif
runner = gst_validate_runner_new ();
monitor =
gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline), runner,
NULL);

View file

@ -537,6 +537,12 @@ main (int argc, gchar ** argv)
g_option_context_free (ctx);
runner = gst_validate_runner_new ();
if (!runner) {
g_printerr ("Failed to setup Validate Runner\n");
exit (1);
}
/* Create the pipeline */
argvn = g_new0 (char *, argc);
memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1));
@ -546,6 +552,8 @@ main (int argc, gchar ** argv)
g_print ("Failed to create pipeline: %s\n",
err ? err->message : "unknown reason");
g_clear_error (&err);
g_object_unref (runner);
exit (1);
}
if (!GST_IS_PIPELINE (pipeline)) {
@ -565,12 +573,6 @@ main (int argc, gchar ** argv)
_register_playbin_actions ();
}
runner = gst_validate_runner_new ();
if (!runner) {
g_printerr ("Failed to setup Validate Runner\n");
exit (1);
}
monitor = gst_validate_monitor_factory_create (GST_OBJECT_CAST (pipeline),
runner, NULL);
gst_validate_reporter_set_handle_g_logs (GST_VALIDATE_REPORTER (monitor));