From cbb9e0b1ec169269733bcd689294b1fd8be59a2c Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Thu, 19 Jan 2017 13:18:55 -0500 Subject: [PATCH] Add bt_plugin test MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This new test proves that the bt_plugin subsystem works as expected. There are three Babeltrace plugins built in `tests/lib/test-plugin-plugins`. They are: * `minimal`: Minimal, valid plugin with no component classes. * `sfs`: Valid plugin with a source, a filter, and a sink component class. * `invalid`: Invalid plugin (missing name). Verified in this test: * Behaviour of bt_plugin_*() functions with non-existing paths, invalid arguments, etc. * bt_plugin_create_from_file() can load a valid plugin and we can access all its properties. * The initialization and exit functions of a plugin are called when expected. * We can access the component classes of a plugin which provides some. * We can still create a component from a component class provided by a plugin after the associated plugin object is destroyed, that is, the associated shared library handle is not closed until it is known that no more user code found in the loaded object will ever be executed again in the future. * bt_plugin_create_all_from_dir() works as expected. As of this patch, Valgrind shows for this test: ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) Signed-off-by: Philippe Proulx Signed-off-by: Jérémie Galarneau --- configure.ac | 2 + tests/lib/Makefile.am | 10 +- tests/lib/test-plugin-plugins/Makefile.am | 34 ++++ tests/lib/test-plugin-plugins/invalid.c | 34 ++++ tests/lib/test-plugin-plugins/minimal.c | 42 ++++ tests/lib/test-plugin-plugins/sfs.c | 61 ++++++ tests/lib/test_plugin.c | 232 ++++++++++++++++++++++ tests/lib/test_plugin_complete.in | 22 ++ 8 files changed, 434 insertions(+), 3 deletions(-) create mode 100644 tests/lib/test-plugin-plugins/Makefile.am create mode 100644 tests/lib/test-plugin-plugins/invalid.c create mode 100644 tests/lib/test-plugin-plugins/minimal.c create mode 100644 tests/lib/test-plugin-plugins/sfs.c create mode 100644 tests/lib/test_plugin.c create mode 100644 tests/lib/test_plugin_complete.in diff --git a/configure.ac b/configure.ac index 25ad0ac5..632602f2 100644 --- a/configure.ac +++ b/configure.ac @@ -417,6 +417,7 @@ AC_CONFIG_FILES([ tests/bin/intersection/Makefile tests/lib/Makefile tests/lib/writer/Makefile + tests/lib/test-plugin-plugins/Makefile tests/utils/Makefile tests/utils/tap/Makefile extras/Makefile @@ -439,6 +440,7 @@ AC_CONFIG_FILES([ #AC_CONFIG_FILES([converter/babeltrace], [chmod +x converter/babeltrace]) AC_CONFIG_FILES([tests/lib/test_ctf_writer_complete], [chmod +x tests/lib/test_ctf_writer_complete]) +AC_CONFIG_FILES([tests/lib/test_plugin_complete], [chmod +x tests/lib/test_plugin_complete]) AC_CONFIG_FILES([tests/lib/test_seek_big_trace], [chmod +x tests/lib/test_seek_big_trace]) AC_CONFIG_FILES([tests/lib/test_seek_empty_packet], [chmod +x tests/lib/test_seek_empty_packet]) AC_CONFIG_FILES([tests/lib/test_dwarf_complete], [chmod +x tests/lib/test_dwarf_complete]) diff --git a/tests/lib/Makefile.am b/tests/lib/Makefile.am index 8fb19108..d02d1496 100644 --- a/tests/lib/Makefile.am +++ b/tests/lib/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = writer +SUBDIRS = test-plugin-plugins writer . AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include -I$(top_srcdir)/tests/utils @@ -34,9 +34,11 @@ test_trace_listener_LDADD = $(COMMON_TEST_LDADD) test_bt_notification_heap_LDADD = $(COMMON_TEST_LDADD) +test_plugin_LDADD = $(COMMON_TEST_LDADD) + noinst_PROGRAMS = test_seek test_bitfield test_ctf_writer test_bt_values \ test_ctf_ir_ref test_bt_ctf_field_type_validation test_ir_visit \ - test_trace_listener test_bt_notification_heap + test_trace_listener test_bt_notification_heap test_plugin test_seek_SOURCES = test_seek.c test_bitfield_SOURCES = test_bitfield.c @@ -47,10 +49,12 @@ test_bt_ctf_field_type_validation_SOURCES = test_bt_ctf_field_type_validation.c test_ir_visit_SOURCES = test_ir_visit.c test_trace_listener_SOURCES = test_trace_listener.c test_bt_notification_heap_SOURCES = test_bt_notification_heap.c +test_plugin_SOURCES = test_plugin.c check_SCRIPTS = test_seek_big_trace \ test_seek_empty_packet \ - test_ctf_writer_complete + test_ctf_writer_complete \ + test_plugin_complete if ENABLE_DEBUG_INFO test_dwarf_LDFLAGS = -static diff --git a/tests/lib/test-plugin-plugins/Makefile.am b/tests/lib/test-plugin-plugins/Makefile.am new file mode 100644 index 00000000..dc4b5a24 --- /dev/null +++ b/tests/lib/test-plugin-plugins/Makefile.am @@ -0,0 +1,34 @@ +AM_CFLAGS = $(PACKAGE_CFLAGS) -I$(top_srcdir)/include +COMMONLIBADD = \ + $(top_builddir)/lib/libbabeltrace.la \ + $(top_builddir)/formats/ctf/libbabeltrace-ctf.la + +# It seems like Automake won't make Libtool create a shared object +# (which we need here since we're building plugins) with a noinst_ +# destination (no install). Indeed we don't want to install those test +# plugins. Libtool creates a shared object when it gets an -rpath +# argument. In this case, Libtool does not seem to care much about +# the actual argument, so we use / (because Libtool needs an absolute +# path), which in the end is not even part of the linked object's +# rpath. +# +# -module makes it possible to not use the `lib` prefix and it makes +# sure that all symbols are dynamically exported. +COMMONLDFLAGS = -rpath / -avoid-version -module + +noinst_LTLIBRARIES = plugin-minimal.la plugin-sfs.la plugin-invalid.la + +# the minimal plugin +plugin_minimal_la_SOURCES = minimal.c +plugin_minimal_la_LDFLAGS = $(COMMONLDFLAGS) +plugin_minimal_la_LIBADD = $(COMMONLIBADD) + +# source/filter/sink plugin +plugin_sfs_la_SOURCES = sfs.c +plugin_sfs_la_LDFLAGS = $(COMMONLDFLAGS) +plugin_sfs_la_LIBADD = $(COMMONLIBADD) + +# invalid plugin +plugin_invalid_la_SOURCES = invalid.c +plugin_invalid_la_LDFLAGS = $(COMMONLDFLAGS) +plugin_invalid_la_LIBADD = $(COMMONLIBADD) diff --git a/tests/lib/test-plugin-plugins/invalid.c b/tests/lib/test-plugin-plugins/invalid.c new file mode 100644 index 00000000..e57eb686 --- /dev/null +++ b/tests/lib/test-plugin-plugins/invalid.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +static enum bt_plugin_status plugin_init(struct bt_plugin *plugin) +{ + return BT_PLUGIN_STATUS_OK; +} + +static enum bt_plugin_status plugin_exit(void) +{ + return BT_PLUGIN_STATUS_OK; +} + +BT_PLUGIN_DESCRIPTION("Invalid plugin because the name is missing"); +BT_PLUGIN_AUTHOR("Janine Sutto"); +BT_PLUGIN_LICENSE("Beerware"); +BT_PLUGIN_INIT(plugin_init); +BT_PLUGIN_EXIT(plugin_exit); diff --git a/tests/lib/test-plugin-plugins/minimal.c b/tests/lib/test-plugin-plugins/minimal.c new file mode 100644 index 00000000..b99c1fff --- /dev/null +++ b/tests/lib/test-plugin-plugins/minimal.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +extern int test_plugin_init_called; +extern int test_plugin_exit_called; + +static enum bt_plugin_status plugin_init(struct bt_plugin *plugin) +{ + test_plugin_init_called = 1; + + return BT_PLUGIN_STATUS_OK; +} + +static enum bt_plugin_status plugin_exit(void) +{ + test_plugin_exit_called = 1; + + return BT_PLUGIN_STATUS_OK; +} + +BT_PLUGIN_NAME("test-minimal"); +BT_PLUGIN_DESCRIPTION("Minimal Babeltrace plugin with no component classes"); +BT_PLUGIN_AUTHOR("Janine Sutto"); +BT_PLUGIN_LICENSE("Beerware"); +BT_PLUGIN_INIT(plugin_init); +BT_PLUGIN_EXIT(plugin_exit); diff --git a/tests/lib/test-plugin-plugins/sfs.c b/tests/lib/test-plugin-plugins/sfs.c new file mode 100644 index 00000000..3928be23 --- /dev/null +++ b/tests/lib/test-plugin-plugins/sfs.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Philippe Proulx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include + +static int sink_value = 42; + +static enum bt_component_status sink_run(struct bt_component *component) +{ + return BT_COMPONENT_STATUS_OK; +} + +static enum bt_component_status comp_class_sink_init( + struct bt_component *component, struct bt_value *params) +{ + enum bt_component_status ret; + + ret = bt_component_set_private_data(component, &sink_value); + if (ret != BT_COMPONENT_STATUS_OK) { + return BT_COMPONENT_STATUS_ERROR; + } + + ret = bt_component_sink_set_consume_cb(component, sink_run); + if (ret != BT_COMPONENT_STATUS_OK) { + return BT_COMPONENT_STATUS_ERROR; + } + + return BT_COMPONENT_STATUS_OK; +} + +static enum bt_component_status comp_class_dummy_init( + struct bt_component *component, struct bt_value *params) +{ + return BT_COMPONENT_STATUS_OK; +} + +BT_PLUGIN_NAME("test-sfs"); +BT_PLUGIN_DESCRIPTION("Babeltrace plugin with source, sink, and filter component classes"); +BT_PLUGIN_AUTHOR("Janine Sutto"); +BT_PLUGIN_LICENSE("Beerware"); + +BT_PLUGIN_COMPONENT_CLASSES_BEGIN +BT_PLUGIN_COMPONENT_CLASS_SOURCE_ENTRY("source", "A source", comp_class_dummy_init) +BT_PLUGIN_COMPONENT_CLASS_SINK_ENTRY("sink", "A sink", comp_class_sink_init) +BT_PLUGIN_COMPONENT_CLASS_FILTER_ENTRY("filter", "A filter", comp_class_dummy_init) +BT_PLUGIN_COMPONENT_CLASSES_END diff --git a/tests/lib/test_plugin.c b/tests/lib/test_plugin.c new file mode 100644 index 00000000..e5261729 --- /dev/null +++ b/tests/lib/test_plugin.c @@ -0,0 +1,232 @@ +/* + * test_plugin.c + * + * CTF IR Reference Count test + * + * Copyright (c) 2017 Philippe Proulx + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; under version 2 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "tap/tap.h" +#include "common.h" + +#define NR_TESTS 33 +#define NON_EXISTING_PATH "/this/hopefully/does/not/exist/5bc75f8d-0dba-4043-a509-d7984b97e42b.so" + +/* Those symbols are written to by some test plugins */ +int test_plugin_init_called; +int test_plugin_exit_called; + +static void reset_test_plugin_symbols(void) +{ + test_plugin_init_called = 0; + test_plugin_exit_called = 0; +} + +static char *get_test_plugin_path(const char *plugin_dir, + const char *plugin_name) +{ + GString *path = g_string_new(plugin_dir); + char *ret; + + assert(path); + g_string_append_printf(path, "/plugin-%s.so", plugin_name); + ret = path->str; + g_string_free(path, FALSE); + return ret; +} + +static void test_invalid(const char *plugin_dir) +{ + struct bt_plugin *plugin; + char *invalid_path = get_test_plugin_path(plugin_dir, "invalid");; + + assert(invalid_path); + + plugin = bt_plugin_create_from_file(NON_EXISTING_PATH); + ok(!plugin, "bt_plugin_create_from_file() fails with a non-existing file"); + + plugin = bt_plugin_create_from_file(plugin_dir); + ok(!plugin, "bt_plugin_create_from_file() fails with a directory"); + + plugin = bt_plugin_create_from_file(invalid_path); + ok(!plugin, "bt_plugin_create_from_file() fails with an invalid plugin file"); + + ok(!bt_plugin_create_from_file(NULL), + "bt_plugin_create_from_file() handles NULL correctly"); + ok(!bt_plugin_create_all_from_dir(NULL, false), + "bt_plugin_create_all_from_dir() handles NULL correctly"); + ok(!bt_plugin_get_name(NULL), + "bt_plugin_get_name() handles NULL correctly"); + ok(!bt_plugin_get_description(NULL), + "bt_plugin_get_description() handles NULL correctly"); + ok(!bt_plugin_get_author(NULL), + "bt_plugin_get_author() handles NULL correctly"); + ok(!bt_plugin_get_license(NULL), + "bt_plugin_get_license() handles NULL correctly"); + ok(!bt_plugin_get_path(NULL), + "bt_plugin_get_path() handles NULL correctly"); + ok(bt_plugin_get_component_class_count(NULL) < 0, + "bt_plugin_get_component_class_count() handles NULL correctly"); + ok(!bt_plugin_get_component_class(NULL, 0), + "bt_plugin_get_component_class() handles NULL correctly"); + ok(!bt_plugin_get_component_class_by_name_and_type(NULL, NULL, 0), + "bt_plugin_get_component_class_by_name_and_type() handles NULL correctly"); + + free(invalid_path); +} + +static void test_minimal(const char *plugin_dir) +{ + struct bt_plugin *plugin; + char *minimal_path = get_test_plugin_path(plugin_dir, "minimal"); + + assert(minimal_path); + diag("minimal plugin test below"); + + reset_test_plugin_symbols(); + plugin = bt_plugin_create_from_file(minimal_path); + ok(plugin, "bt_plugin_create_from_file() succeeds with a valid file"); + ok(test_plugin_init_called && !test_plugin_exit_called, + "plugin's initialization function is called during bt_plugin_create_from_file()"); + ok(strcmp(bt_plugin_get_name(plugin), "test-minimal") == 0, + "bt_plugin_get_name() returns the expected name"); + ok(strcmp(bt_plugin_get_description(plugin), + "Minimal Babeltrace plugin with no component classes") == 0, + "bt_plugin_get_description() returns the expected description"); + ok(strcmp(bt_plugin_get_author(plugin), "Janine Sutto") == 0, + "bt_plugin_get_author() returns the expected author"); + ok(strcmp(bt_plugin_get_license(plugin), "Beerware") == 0, + "bt_plugin_get_license() returns the expected license"); + ok(strcmp(bt_plugin_get_path(plugin), minimal_path) == 0, + "bt_plugin_get_path() returns the expected path"); + ok(bt_plugin_get_component_class_count(plugin) == 0, + "bt_plugin_get_component_class_count() returns the expected value"); + BT_PUT(plugin); + ok(test_plugin_exit_called, "plugin's exit function is called when the plugin is destroyed"); + + free(minimal_path); +} + +static void test_sfs(const char *plugin_dir) +{ + struct bt_plugin *plugin; + struct bt_component_class *sink_comp_class; + struct bt_component_class *source_comp_class; + struct bt_component_class *filter_comp_class; + struct bt_component *sink_component; + char *sfs_path = get_test_plugin_path(plugin_dir, "sfs"); + + assert(sfs_path); + diag("sfs plugin test below"); + + plugin = bt_plugin_create_from_file(sfs_path); + assert(plugin); + ok(bt_plugin_get_component_class_count(plugin) == 3, + "bt_plugin_get_component_class_count() returns the expected value"); + + source_comp_class = bt_plugin_get_component_class_by_name_and_type( + plugin, "source", BT_COMPONENT_TYPE_SOURCE); + ok(source_comp_class, + "bt_plugin_get_component_class_by_name_and_type() finds a source component class"); + + sink_comp_class = bt_plugin_get_component_class_by_name_and_type( + plugin, "sink", BT_COMPONENT_TYPE_SINK); + + ok(sink_comp_class, + "bt_plugin_get_component_class_by_name_and_type() finds a sink component class"); + filter_comp_class = bt_plugin_get_component_class_by_name_and_type( + plugin, "filter", BT_COMPONENT_TYPE_FILTER); + + ok(filter_comp_class, + "bt_plugin_get_component_class_by_name_and_type() finds a filter component class"); + ok(!bt_plugin_get_component_class_by_name_and_type(plugin, "filter", + BT_COMPONENT_TYPE_SOURCE), + "bt_plugin_get_component_class_by_name_and_type() does not find a component class given the wrong type"); + + diag("> putting the plugin object here"); + BT_PUT(plugin); + sink_component = bt_component_create(sink_comp_class, NULL, bt_value_null); + ok(sink_component, "bt_component_create() still works after the plugin object is destroyed"); + BT_PUT(sink_component); + BT_PUT(source_comp_class); + sink_component = bt_component_create(sink_comp_class, NULL, bt_value_null); + ok(sink_component, "bt_component_create() still works after the source component class object is destroyed"); + BT_PUT(sink_component); + BT_PUT(filter_comp_class); + sink_component = bt_component_create(sink_comp_class, NULL, bt_value_null); + ok(sink_component, "bt_component_create() still works after the filter component class object is destroyed"); + BT_PUT(sink_comp_class); + BT_PUT(sink_component); + + free(sfs_path); +} + +static void test_create_all_from_dir(const char *plugin_dir) +{ + struct bt_plugin **plugins; + struct bt_plugin *plugin; + int i; + + diag("create from all test below"); + + plugins = bt_plugin_create_all_from_dir(NON_EXISTING_PATH, false); + ok(!plugins, + "bt_plugin_create_all_from_dir() fails with an invalid path"); + + plugins = bt_plugin_create_all_from_dir(plugin_dir, false); + ok(plugins, "bt_plugin_create_all_from_dir() succeeds with a valid path"); + + i = 0; + while ((plugin = plugins[i])) { + BT_PUT(plugin); + i++; + } + + /* 2 or 4, if `.la` files are considered or not */ + ok(i == 2 || i == 4, "bt_plugin_create_all_from_dir() returns the expected number of plugin objects"); + + free(plugins); +} + +int main(int argc, char **argv) +{ + int ret; + const char *plugin_dir; + + if (argc != 2) { + puts("Usage: test_plugin plugin_directory"); + ret = 1; + goto end; + } + + plugin_dir = argv[1]; + plan_tests(NR_TESTS); + test_invalid(plugin_dir); + test_minimal(plugin_dir); + test_sfs(plugin_dir); + test_create_all_from_dir(plugin_dir); + ret = exit_status(); +end: + return ret; +} diff --git a/tests/lib/test_plugin_complete.in b/tests/lib/test_plugin_complete.in new file mode 100644 index 00000000..4936fee7 --- /dev/null +++ b/tests/lib/test_plugin_complete.in @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Copyright (C) 2017 Philippe Proulx +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; only version 2 +# of the License. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +PLUGIN_DIR="@abs_top_builddir@/tests/lib/test-plugin-plugins/.libs" + +"@abs_top_builddir@/tests/lib/test_plugin" "$PLUGIN_DIR" -- 2.34.1