From: Simon Marchi Date: Thu, 9 Apr 2020 20:23:47 +0000 (-0400) Subject: lib, bt2: add precondition check for port name unicity X-Git-Url: http://git.efficios.com/?p=babeltrace.git;a=commitdiff_plain;h=157a98edd5aebe1b6ab7f60a49d8430450fabe76 lib, bt2: add precondition check for port name unicity 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 Reviewed-on: https://review.lttng.org/c/babeltrace/+/3387 Reviewed-by: Philippe Proulx Tested-by: jenkins --- diff --git a/src/bindings/python/bt2/bt2/component.py b/src/bindings/python/bt2/bt2/component.py index 97446a83..4e079f77 100644 --- a/src/bindings/python/bt2/bt2/component.py +++ b/src/bindings/python/bt2/bt2/component.py @@ -821,6 +821,14 @@ class _UserSourceComponent(_UserComponent, _SourceComponentConst): 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( @@ -871,6 +879,14 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst): 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( @@ -883,6 +899,14 @@ class _UserFilterComponent(_UserComponent, _FilterComponentConst): 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( @@ -925,6 +949,14 @@ class _UserSinkComponent(_UserComponent, _SinkComponentConst): 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( diff --git a/src/lib/graph/component-filter.c b/src/lib/graph/component-filter.c index cfbb9640..3541514f 100644 --- a/src/lib/graph/component-filter.c +++ b/src/lib/graph/component-filter.c @@ -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(); + 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); @@ -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(); + 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); diff --git a/src/lib/graph/component-sink.c b/src/lib/graph/component-sink.c index f3093b2a..1f4a5a66 100644 --- a/src/lib/graph/component-sink.c +++ b/src/lib/graph/component-sink.c @@ -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(); + 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); diff --git a/src/lib/graph/component-source.c b/src/lib/graph/component-source.c index ed3da160..879af724 100644 --- a/src/lib/graph/component-source.c +++ b/src/lib/graph/component-source.c @@ -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(); + 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); diff --git a/src/lib/graph/component.c b/src/lib/graph/component.c index 26da38be..d42dba7f 100644 --- a/src/lib/graph/component.c +++ b/src/lib/graph/component.c @@ -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_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( diff --git a/src/lib/graph/component.h b/src/lib/graph/component.h index 41254539..387996aa 100644 --- a/src/lib/graph/component.h +++ b/src/lib/graph/component.h @@ -22,6 +22,14 @@ #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); @@ -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); +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); diff --git a/tests/bindings/python/bt2/test_port.py b/tests/bindings/python/bt2/test_port.py index c4b7674c..e6db06ea 100644 --- a/tests/bindings/python/bt2/test_port.py +++ b/tests/bindings/python/bt2/test_port.py @@ -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) + # 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 @@ -41,6 +62,27 @@ class PortTestCase(unittest.TestCase): 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 @@ -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) + # 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): @@ -65,6 +128,28 @@ class PortTestCase(unittest.TestCase): 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