Add bt_plugin test
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 19 Jan 2017 18:18:55 +0000 (13:18 -0500)
committerJérémie Galarneau <jeremie.galarneau@efficios.com>
Sun, 28 May 2017 16:57:37 +0000 (12:57 -0400)
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 <eeppeliteloop@gmail.com>
Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
configure.ac
tests/lib/Makefile.am
tests/lib/test-plugin-plugins/Makefile.am [new file with mode: 0644]
tests/lib/test-plugin-plugins/invalid.c [new file with mode: 0644]
tests/lib/test-plugin-plugins/minimal.c [new file with mode: 0644]
tests/lib/test-plugin-plugins/sfs.c [new file with mode: 0644]
tests/lib/test_plugin.c [new file with mode: 0644]
tests/lib/test_plugin_complete.in [new file with mode: 0644]

index 25ad0ac582091cfaae96bdbfac880e55f0e084be..632602f2a47fb2a21fea6945ab9503bda5e45f65 100644 (file)
@@ -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])
index 8fb1910861f11da2b655f6a1b5a163a7d5ced8ee..d02d14960d615d3127e4841e0047476efd6b69e1 100644 (file)
@@ -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 (file)
index 0000000..dc4b5a2
--- /dev/null
@@ -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 (file)
index 0000000..e57eb68
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace/plugin/plugin-dev.h>
+
+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 (file)
index 0000000..b99c1ff
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace/plugin/plugin-dev.h>
+
+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 (file)
index 0000000..3928be2
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace/plugin/plugin-dev.h>
+#include <babeltrace/component/component.h>
+
+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 (file)
index 0000000..e526172
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * test_plugin.c
+ *
+ * CTF IR Reference Count test
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * 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 <babeltrace/plugin/plugin.h>
+#include <babeltrace/ref.h>
+#include <babeltrace/values.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+#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 (file)
index 0000000..4936fee
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# Copyright (C) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+# 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"
This page took 0.03124 seconds and 4 git commands to generate.