lib, bt2: add precondition check for port name unicity
authorSimon Marchi <simon.marchi@efficios.com>
Thu, 9 Apr 2020 20:23:47 +0000 (16:23 -0400)
committerSimon Marchi <simon.marchi@efficios.com>
Tue, 14 Apr 2020 14:56:41 +0000 (10:56 -0400)
The documentation for the _add_input_port API functions state that there
must be no other input port with the same name.  Ditto for output ports.

This patch adds some precondition checks for that.  A failed assertion looks
like this:

    04-09 16:29:23.968 2961462 2961462 F LIB/COMPONENT-SINK bt_self_component_sink_add_input_port@component-sink.c:132 Babeltrace 2 library precondition not satisfied; error is:
    04-09 16:29:23.968 2961462 2961462 F LIB/COMPONENT-SINK bt_self_component_sink_add_input_port@component-sink.c:132 Input port name is not unique: name="bob", comp-addr=0x60c000001e40, comp-name="mon sink", comp-log-level=NONE, comp-class-type=SINK, comp-class-name="MySink", comp-class-partial-descr=""
    04-09 16:29:23.968 2961462 2961462 F LIB/COMPONENT-SINK bt_self_component_sink_add_input_port@component-sink.c:132 Aborting...

Equivalent checks are added to the Python bindings, as well as tests.

Change-Id: I12f5a16b6b5efc46f2b50f27e338cc57b8487ff6
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/3387
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
Tested-by: jenkins <jenkins@lttng.org>
src/bindings/python/bt2/bt2/component.py
src/lib/graph/component-filter.c
src/lib/graph/component-sink.c
src/lib/graph/component-source.c
src/lib/graph/component.c
src/lib/graph/component.h
tests/bindings/python/bt2/test_port.py

index 97446a830e5d902359e171830e33f77a8ecf0df2..4e079f7743d934f1b6413efe35c0255028cf0b86 100644 (file)
@@ -821,6 +821,14 @@ class _UserSourceComponent(_UserComponent, _SourceComponentConst):
 
     def _add_output_port(self, name, user_data=None):
         utils._check_str(name)
 
     def _add_output_port(self, name, user_data=None):
         utils._check_str(name)
+
+        if name in self._output_ports:
+            raise ValueError(
+                'source component `{}` already contains an output port named `{}`'.format(
+                    self.name, name
+                )
+            )
+
         fn = native_bt.self_component_source_add_output_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
         fn = native_bt.self_component_source_add_output_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
@@ -871,6 +879,14 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst):
 
     def _add_output_port(self, name, user_data=None):
         utils._check_str(name)
 
     def _add_output_port(self, name, user_data=None):
         utils._check_str(name)
+
+        if name in self._output_ports:
+            raise ValueError(
+                'filter component `{}` already contains an output port named `{}`'.format(
+                    self.name, name
+                )
+            )
+
         fn = native_bt.self_component_filter_add_output_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
         fn = native_bt.self_component_filter_add_output_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
@@ -883,6 +899,14 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst):
 
     def _add_input_port(self, name, user_data=None):
         utils._check_str(name)
 
     def _add_input_port(self, name, user_data=None):
         utils._check_str(name)
+
+        if name in self._input_ports:
+            raise ValueError(
+                'filter component `{}` already contains an input port named `{}`'.format(
+                    self.name, name
+                )
+            )
+
         fn = native_bt.self_component_filter_add_input_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
         fn = native_bt.self_component_filter_add_input_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
@@ -925,6 +949,14 @@ class _UserSinkComponent(_UserComponent, _SinkComponentConst):
 
     def _add_input_port(self, name, user_data=None):
         utils._check_str(name)
 
     def _add_input_port(self, name, user_data=None):
         utils._check_str(name)
+
+        if name in self._input_ports:
+            raise ValueError(
+                'sink component `{}` already contains an input port named `{}`'.format(
+                    self.name, name
+                )
+            )
+
         fn = native_bt.self_component_sink_add_input_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
         fn = native_bt.self_component_sink_add_input_port
         comp_status, self_port_ptr = fn(self._bt_ptr, name, user_data)
         utils._handle_func_status(
index cfbb9640096cc94282853b95d5a95ea0b7a3a9b6..3541514fbac87009dba6c22ef98847f1b8c1369f 100644 (file)
@@ -109,6 +109,7 @@ enum bt_self_component_add_port_status bt_self_component_filter_add_output_port(
        struct bt_port *port = NULL;
 
        BT_ASSERT_PRE_NO_ERROR();
        struct bt_port *port = NULL;
 
        BT_ASSERT_PRE_NO_ERROR();
+       BT_ASSERT_PRE_OUTPUT_PORT_NAME_UNIQUE(comp, name);
 
        /* bt_component_add_output_port() logs details and errors */
        status = bt_component_add_output_port(comp, name, user_data, &port);
 
        /* bt_component_add_output_port() logs details and errors */
        status = bt_component_add_output_port(comp, name, user_data, &port);
@@ -178,6 +179,7 @@ enum bt_self_component_add_port_status bt_self_component_filter_add_input_port(
        struct bt_component *comp = (void *) self_comp;
 
        BT_ASSERT_PRE_NO_ERROR();
        struct bt_component *comp = (void *) self_comp;
 
        BT_ASSERT_PRE_NO_ERROR();
+       BT_ASSERT_PRE_INPUT_PORT_NAME_UNIQUE(comp, name);
 
        /* bt_component_add_input_port() logs details/errors */
        status = bt_component_add_input_port(comp, name, user_data, &port);
 
        /* bt_component_add_input_port() logs details/errors */
        status = bt_component_add_input_port(comp, name, user_data, &port);
index f3093b2ac3f3ffbdf63ee4cf7bf2c537ae8ba422..1f4a5a66589533e65d83c10d5849788f31058f37 100644 (file)
@@ -113,6 +113,7 @@ enum bt_self_component_add_port_status bt_self_component_sink_add_input_port(
        struct bt_component *comp = (void *) self_comp;
 
        BT_ASSERT_PRE_NO_ERROR();
        struct bt_component *comp = (void *) self_comp;
 
        BT_ASSERT_PRE_NO_ERROR();
+       BT_ASSERT_PRE_INPUT_PORT_NAME_UNIQUE(comp, name);
 
        /* bt_component_add_input_port() logs details/errors */
        status = bt_component_add_input_port(comp, name, user_data, &port);
 
        /* bt_component_add_input_port() logs details/errors */
        status = bt_component_add_input_port(comp, name, user_data, &port);
index ed3da16016767df8d9be72b6f8936489a602780c..879af72474e0fa1a91371c46e1cd82b23f9f21e4 100644 (file)
@@ -107,6 +107,7 @@ enum bt_self_component_add_port_status bt_self_component_source_add_output_port(
        struct bt_port *port = NULL;
 
        BT_ASSERT_PRE_NO_ERROR();
        struct bt_port *port = NULL;
 
        BT_ASSERT_PRE_NO_ERROR();
+       BT_ASSERT_PRE_OUTPUT_PORT_NAME_UNIQUE(comp, name);
 
        /* bt_component_add_output_port() logs details and errors */
        status = bt_component_add_output_port(comp, name, user_data, &port);
 
        /* bt_component_add_output_port() logs details and errors */
        status = bt_component_add_output_port(comp, name, user_data, &port);
index 26da38be78524a77c578898fd8d4a259a2f2b18e..d42dba7fc1ae8f6363f24a1743ade06102cdf792 100644 (file)
@@ -464,6 +464,27 @@ enum bt_self_component_add_port_status bt_component_add_output_port(
                BT_PORT_TYPE_OUTPUT, name, user_data, port);
 }
 
                BT_PORT_TYPE_OUTPUT, name, user_data, port);
 }
 
+BT_HIDDEN
+bool bt_component_port_name_is_unique(GPtrArray *ports, const char *name)
+{
+       guint i;
+       bool unique;
+
+       for (i = 0; i < ports->len; i++) {
+               struct bt_port *port = g_ptr_array_index(ports, i);
+
+               if (strcmp(port->name->str, name) == 0) {
+                       unique = false;
+                       goto end;
+               }
+       }
+
+       unique = true;
+
+end:
+       return unique;
+}
+
 BT_HIDDEN
 enum bt_component_class_port_connected_method_status
 bt_component_port_connected(
 BT_HIDDEN
 enum bt_component_class_port_connected_method_status
 bt_component_port_connected(
index 4125453979bb0dc8e6e3ff66f3953f3ef47a3b7e..387996aac4ad5e592a8a35a3ebdbc57101898f2c 100644 (file)
 #include "component-class.h"
 #include "port.h"
 
 #include "component-class.h"
 #include "port.h"
 
+#define BT_ASSERT_PRE_INPUT_PORT_NAME_UNIQUE(comp, name) \
+       BT_ASSERT_PRE(bt_component_port_name_is_unique(comp->input_ports, name), \
+               "Input port name is not unique: name=\"%s\", %![comp-]c", name, comp);
+
+#define BT_ASSERT_PRE_OUTPUT_PORT_NAME_UNIQUE(comp, name) \
+       BT_ASSERT_PRE(bt_component_port_name_is_unique(comp->output_ports, name), \
+               "Output port name is not unique: name=\"%s\", %![comp-]c", name, comp);
+
 typedef void (*bt_component_destroy_listener_func)(
                struct bt_component *class, void *data);
 
 typedef void (*bt_component_destroy_listener_func)(
                struct bt_component *class, void *data);
 
@@ -111,6 +119,9 @@ enum bt_self_component_add_port_status bt_component_add_output_port(
                struct bt_component *component, const char *name,
                void *user_data, struct bt_port **port);
 
                struct bt_component *component, const char *name,
                void *user_data, struct bt_port **port);
 
+BT_HIDDEN
+bool bt_component_port_name_is_unique(GPtrArray *ports, const char *name);
+
 BT_HIDDEN
 void bt_component_remove_port(struct bt_component *component,
                struct bt_port *port);
 BT_HIDDEN
 void bt_component_remove_port(struct bt_component *component,
                struct bt_port *port);
index c4b7674c0960bf95c31e7b628bfaa658896a4391..e6db06ea53c0736b3cf4facaa86e869179789483 100644 (file)
@@ -30,6 +30,27 @@ class PortTestCase(unittest.TestCase):
         self.assertEqual(len(comp.output_ports), 1)
         self.assertIs(type(comp.output_ports['out']), bt2_port._OutputPortConst)
 
         self.assertEqual(len(comp.output_ports), 1)
         self.assertIs(type(comp.output_ports['out']), bt2_port._OutputPortConst)
 
+    # Test adding output port with duplicate name to source.
+    def test_src_add_output_port_dup_name_raises(self):
+        class MySource(
+            bt2._UserSourceComponent, message_iterator_class=bt2._UserMessageIterator
+        ):
+            def __init__(comp_self, config, params, obj):
+                comp_self._add_output_port('out')
+
+                with self.assertRaisesRegex(
+                    ValueError,
+                    "source component `comp` already contains an output port named `out`",
+                ):
+                    comp_self._add_output_port('out')
+
+                nonlocal seen
+                seen = True
+
+        seen = False
+        self._create_comp(MySource)
+        self.assertTrue(seen)
+
     def test_flt_add_output_port(self):
         class MyFilter(
             bt2._UserFilterComponent, message_iterator_class=bt2._UserMessageIterator
     def test_flt_add_output_port(self):
         class MyFilter(
             bt2._UserFilterComponent, message_iterator_class=bt2._UserMessageIterator
@@ -41,6 +62,27 @@ class PortTestCase(unittest.TestCase):
         comp = self._create_comp(MyFilter)
         self.assertEqual(len(comp.output_ports), 1)
 
         comp = self._create_comp(MyFilter)
         self.assertEqual(len(comp.output_ports), 1)
 
+    # Test adding output port with duplicate name to filter.
+    def test_flt_add_output_port_dup_name_raises(self):
+        class MyFilter(
+            bt2._UserFilterComponent, message_iterator_class=bt2._UserMessageIterator
+        ):
+            def __init__(comp_self, config, params, obj):
+                comp_self._add_output_port('out')
+
+                with self.assertRaisesRegex(
+                    ValueError,
+                    "filter component `comp` already contains an output port named `out`",
+                ):
+                    comp_self._add_output_port('out')
+
+                nonlocal seen
+                seen = True
+
+        seen = False
+        self._create_comp(MyFilter)
+        self.assertTrue(seen)
+
     def test_flt_add_input_port(self):
         class MyFilter(
             bt2._UserFilterComponent, message_iterator_class=bt2._UserMessageIterator
     def test_flt_add_input_port(self):
         class MyFilter(
             bt2._UserFilterComponent, message_iterator_class=bt2._UserMessageIterator
@@ -53,6 +95,27 @@ class PortTestCase(unittest.TestCase):
         self.assertEqual(len(comp.input_ports), 1)
         self.assertIs(type(comp.input_ports['in']), bt2_port._InputPortConst)
 
         self.assertEqual(len(comp.input_ports), 1)
         self.assertIs(type(comp.input_ports['in']), bt2_port._InputPortConst)
 
+    # Test adding input port with duplicate name to filter.
+    def test_flt_add_input_port_dup_name_raises(self):
+        class MyFilter(
+            bt2._UserFilterComponent, message_iterator_class=bt2._UserMessageIterator
+        ):
+            def __init__(comp_self, config, params, obj):
+                comp_self._add_input_port('in')
+
+                with self.assertRaisesRegex(
+                    ValueError,
+                    "filter component `comp` already contains an input port named `in`",
+                ):
+                    comp_self._add_input_port('in')
+
+                nonlocal seen
+                seen = True
+
+        seen = False
+        self._create_comp(MyFilter)
+        self.assertTrue(seen)
+
     def test_sink_add_input_port(self):
         class MySink(bt2._UserSinkComponent):
             def __init__(comp_self, config, params, obj):
     def test_sink_add_input_port(self):
         class MySink(bt2._UserSinkComponent):
             def __init__(comp_self, config, params, obj):
@@ -65,6 +128,28 @@ class PortTestCase(unittest.TestCase):
         comp = self._create_comp(MySink)
         self.assertEqual(len(comp.input_ports), 1)
 
         comp = self._create_comp(MySink)
         self.assertEqual(len(comp.input_ports), 1)
 
+    # Test adding input port with duplicate name to sink.
+    def test_sink_add_input_port_dup_name_raises(self):
+        class MySink(bt2._UserSinkComponent):
+            def __init__(comp_self, config, params, obj):
+                comp_self._add_input_port('in')
+
+                with self.assertRaisesRegex(
+                    ValueError,
+                    "sink component `comp` already contains an input port named `in`",
+                ):
+                    comp_self._add_input_port('in')
+
+                nonlocal seen
+                seen = True
+
+            def _user_consume(self):
+                pass
+
+        seen = False
+        self._create_comp(MySink)
+        self.assertTrue(seen)
+
     def test_user_src_output_ports_getitem(self):
         class MySource(
             bt2._UserSourceComponent, message_iterator_class=bt2._UserMessageIterator
     def test_user_src_output_ports_getitem(self):
         class MySource(
             bt2._UserSourceComponent, message_iterator_class=bt2._UserMessageIterator
This page took 0.029623 seconds and 4 git commands to generate.