bt2: add integer range set support
authorPhilippe Proulx <eeppeliteloop@gmail.com>
Fri, 12 Jul 2019 18:04:54 +0000 (14:04 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Thu, 18 Jul 2019 13:42:13 +0000 (09:42 -0400)
This patch adds `bt2` wrappers for the Babeltrace library integer range
and integer range set library objects.

The new `test_integer_range_set.py` file tests all the new objects.

Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
Change-Id: I9e6852ca2ec6b04bcbc3b6622c03c3abcfa6e32f
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1716

src/bindings/python/bt2/Makefile.am
src/bindings/python/bt2/bt2/__init__.py.in
src/bindings/python/bt2/bt2/integer_range_set.py [new file with mode: 0644]
src/bindings/python/bt2/bt2/native_bt.i
src/bindings/python/bt2/bt2/native_bt_integer_range_set.i [new file with mode: 0644]
tests/Makefile.am
tests/bindings/python/bt2/test_integer_range_set.py [new file with mode: 0644]

index 799670cd04bd22a5ab30b620d0dbe01baca2c6ef..0324ed2483804129a5d83326e4ea2440757e51be 100644 (file)
@@ -18,6 +18,8 @@ SWIG_INTERFACE_FILES =                                        \
        bt2/native_bt_field_path.i                      \
        bt2/native_bt_field.i                           \
        bt2/native_bt_graph.i                           \
+       bt2/native_bt_integer_range_set.i               \
+       bt2/native_bt.i                                 \
        bt2/native_bt_logging.i                         \
        bt2/native_bt_message.i                         \
        bt2/native_bt_message_iterator.i                \
@@ -48,6 +50,7 @@ STATIC_BINDINGS_DEPS =                                        \
        bt2/field_class.py                              \
        bt2/field_path.py                               \
        bt2/graph.py                                    \
+       bt2/integer_range_set.py                        \
        bt2/logging.py                                  \
        bt2/message_iterator.py                         \
        bt2/message.py                                  \
index 37c7e4bee1119cc2468c891a1e214ce92a0770a6..1e5b8ee38c848c5081260361df6f7bd0bcbd9d62 100644 (file)
@@ -43,6 +43,7 @@ from bt2.field_class import *
 from bt2.field_path import *
 from bt2.field import *
 from bt2.graph import *
+from bt2.integer_range_set import *
 from bt2.logging import *
 from bt2.message import *
 from bt2.message import _DiscardedEventsMessage
diff --git a/src/bindings/python/bt2/bt2/integer_range_set.py b/src/bindings/python/bt2/bt2/integer_range_set.py
new file mode 100644 (file)
index 0000000..2e79da0
--- /dev/null
@@ -0,0 +1,150 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+from bt2 import native_bt, object, utils
+import collections.abc
+import bt2
+
+
+class _IntegerRange:
+    def __init__(self, lower, upper):
+        self._check_type(lower)
+        self._check_type(upper)
+
+        if lower > upper:
+            raise ValueError("range's lower bound ({}) is greater than its upper bound ({})".format(lower, upper))
+
+        self._lower = lower
+        self._upper = upper
+
+    @property
+    def lower(self):
+        return self._lower
+
+    @property
+    def upper(self):
+        return self._upper
+
+    def contains(self, value):
+        self._check_type(value)
+        return value >= self._lower and value <= self._upper
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        return self.lower == other.lower and self.upper == other.upper
+
+
+class SignedIntegerRange(_IntegerRange):
+    _check_type = staticmethod(utils._check_int64)
+
+
+class UnsignedIntegerRange(_IntegerRange):
+    _check_type = staticmethod(utils._check_uint64)
+
+
+class _IntegerRangeSet(object._SharedObject, collections.abc.MutableSet):
+    def __init__(self, ranges=None):
+        ptr = self._create_range_set()
+
+        if ptr is None:
+            raise bt2.CreationError('cannot create range set object')
+
+        super().__init__(ptr)
+
+        if ranges is not None:
+            # will raise if not iterable
+            for rg in ranges:
+                self.add(rg)
+
+    def __len__(self):
+        range_set_ptr = self._as_range_set_ptr(self._ptr)
+        count = native_bt.integer_range_set_get_range_count(range_set_ptr)
+        assert count >= 0
+        return count
+
+    def __contains__(self, other_range):
+        for rg in self:
+            if rg == other_range:
+                return True
+
+        return False
+
+    def __iter__(self):
+        for idx in range(len(self)):
+            rg_ptr = self._borrow_range_by_index_ptr(self._ptr, idx)
+            assert rg_ptr is not None
+            lower = self._range_get_lower(rg_ptr)
+            upper = self._range_get_upper(rg_ptr)
+            yield self._range_type(lower, upper)
+
+    def __eq__(self, other):
+        if type(other) is not type(self):
+            return False
+
+        return self._compare(self._ptr, other._ptr)
+
+    def contains_value(self, value):
+        for rg in self:
+            if rg.contains(value):
+                return True
+
+        return False
+
+    def add(self, rg):
+        if type(rg) is not self._range_type:
+            # assume it's a simple pair (will raise if it's not)
+            rg = self._range_type(rg[0], rg[1])
+
+        status = self._add_range(self._ptr, rg.lower, rg.upper)
+        utils._handle_func_status(status,
+                                  'cannot add range to range set object')
+
+    def discard(self, rg):
+        raise NotImplementedError
+
+
+class SignedIntegerRangeSet(_IntegerRangeSet):
+    _get_ref = staticmethod(native_bt.integer_range_set_signed_get_ref)
+    _put_ref = staticmethod(native_bt.integer_range_set_signed_put_ref)
+    _as_range_set_ptr = staticmethod(native_bt.integer_range_set_signed_as_range_set_const)
+    _create_range_set = staticmethod(native_bt.integer_range_set_signed_create)
+    _borrow_range_by_index_ptr = staticmethod(native_bt.integer_range_set_signed_borrow_range_by_index_const)
+    _range_get_lower = staticmethod(native_bt.integer_range_signed_get_lower)
+    _range_get_upper = staticmethod(native_bt.integer_range_signed_get_upper)
+    _add_range = staticmethod(native_bt.integer_range_set_signed_add_range)
+    _compare = staticmethod(native_bt.integer_range_set_signed_compare)
+    _range_type = SignedIntegerRange
+
+
+class UnsignedIntegerRangeSet(_IntegerRangeSet):
+    _get_ref = staticmethod(native_bt.integer_range_set_unsigned_get_ref)
+    _put_ref = staticmethod(native_bt.integer_range_set_unsigned_put_ref)
+    _as_range_set_ptr = staticmethod(native_bt.integer_range_set_unsigned_as_range_set_const)
+    _create_range_set = staticmethod(native_bt.integer_range_set_unsigned_create)
+    _borrow_range_by_index_ptr = staticmethod(native_bt.integer_range_set_unsigned_borrow_range_by_index_const)
+    _range_get_lower = staticmethod(native_bt.integer_range_unsigned_get_lower)
+    _range_get_upper = staticmethod(native_bt.integer_range_unsigned_get_upper)
+    _add_range = staticmethod(native_bt.integer_range_set_unsigned_add_range)
+    _compare = staticmethod(native_bt.integer_range_set_unsigned_compare)
+    _range_type = UnsignedIntegerRange
index c87a5e7280ca3300600878515df709b2a4f200dc..49adfbfdf9b410fd6f719f48e5d58706c3bb9cdd 100644 (file)
@@ -216,6 +216,7 @@ typedef int bt_bool;
 %include "native_bt_plugin.i"
 %include "native_bt_port.i"
 %include "native_bt_query_exec.i"
+%include "native_bt_integer_range_set.i"
 %include "native_bt_stream.i"
 %include "native_bt_stream_class.i"
 %include "native_bt_trace.i"
diff --git a/src/bindings/python/bt2/bt2/native_bt_integer_range_set.i b/src/bindings/python/bt2/bt2/native_bt_integer_range_set.i
new file mode 100644 (file)
index 0000000..f3066e1
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2017 Philippe Proulx <pproulx@efficios.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * Typemap for the user data attached to (and owned by) a self component port.
+ * The pointer saved as the port's user data is directly the PyObject *.
+ *
+ * As per the CPython calling convention, we need to return a new reference to
+ * the returned object, which will be transferred to the caller.
+ */
+
+%include <babeltrace2/integer-range-set.h>
+%include <babeltrace2/integer-range-set-const.h>
index db0ecaef828c498c07bdd035490c5ec45fb6ac02..e5f36bac31b7202af0a2c11e3a1012a44eb81956 100644 (file)
@@ -15,6 +15,7 @@ dist_check_SCRIPTS = \
        bindings/python/bt2/test_field_class.py \
        bindings/python/bt2/test_field.py \
        bindings/python/bt2/test_graph.py \
+       bindings/python/bt2/test_integer_range_set.py \
        bindings/python/bt2/test_message_iterator.py \
        bindings/python/bt2/test_message.py \
        bindings/python/bt2/test_packet.py \
diff --git a/tests/bindings/python/bt2/test_integer_range_set.py b/tests/bindings/python/bt2/test_integer_range_set.py
new file mode 100644 (file)
index 0000000..2a9c5f6
--- /dev/null
@@ -0,0 +1,213 @@
+#
+# Copyright (C) 2019 EfficiOS Inc.
+#
+# 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.
+#
+
+import bt2
+import unittest
+
+
+class _IntegerRangeTestCase:
+    def setUp(self):
+        self._rg = self._CLS(self._def_lower, self._def_upper)
+
+    def test_create(self):
+        self.assertEqual(self._rg.lower, self._def_lower)
+        self.assertEqual(self._rg.upper, self._def_upper)
+
+    def test_create_same(self):
+        rg = self._CLS(self._def_lower, self._def_lower)
+        self.assertEqual(rg.lower, self._def_lower)
+        self.assertEqual(rg.upper, self._def_lower)
+
+    def test_create_wrong_type_lower(self):
+        with self.assertRaises(TypeError):
+            rg = self._CLS(19.3, self._def_upper)
+
+    def test_create_wrong_type_lower(self):
+        with self.assertRaises(TypeError):
+            rg = self._CLS(self._def_lower, 19.3)
+
+    def test_create_out_of_bound_lower(self):
+        with self.assertRaises(ValueError):
+            rg = self._CLS(self._oob_lower, self._def_upper)
+
+    def test_create_out_of_bound_upper(self):
+        with self.assertRaises(ValueError):
+            rg = self._CLS(self._def_lower, self._oob_upper)
+
+    def test_create_lower_gt_upper(self):
+        with self.assertRaises(ValueError):
+            rg = self._CLS(self._def_lower, self._def_lower - 1)
+
+    def test_contains_lower(self):
+        self.assertTrue(self._rg.contains(self._def_lower))
+
+    def test_contains_upper(self):
+        self.assertTrue(self._rg.contains(self._def_upper))
+
+    def test_contains_avg(self):
+        avg = (self._def_lower + self._def_upper) // 2
+        self.assertTrue(self._rg.contains(avg))
+
+    def test_contains_wrong_type(self):
+        with self.assertRaises(TypeError):
+            self._rg.contains('allo')
+
+    def test_contains_out_of_bound(self):
+        with self.assertRaises(ValueError):
+            self._rg.contains(self._oob_upper)
+
+    def test_eq(self):
+        rg = self._CLS(self._def_lower, self._def_upper)
+        self.assertEqual(rg, self._rg)
+
+    def test_ne(self):
+        rg = self._CLS(self._def_lower, self._def_upper - 1)
+        self.assertNotEqual(rg, self._rg)
+
+    def test_ne_other_type(self):
+        self.assertNotEqual(self._rg, 48)
+
+
+class UnsignedIntegerRangeTestCase(_IntegerRangeTestCase, unittest.TestCase):
+    _CLS = bt2.UnsignedIntegerRange
+    _def_lower = 23
+    _def_upper = 18293
+    _oob_lower = -1
+    _oob_upper = 1 << 64
+
+
+class SignedIntegerRangeTestCase(_IntegerRangeTestCase, unittest.TestCase):
+    _CLS = bt2.SignedIntegerRange
+    _def_lower = -184
+    _def_upper = 11547
+    _oob_lower = -(1 << 63) - 1
+    _oob_upper = 1 << 63
+
+
+class _IntegerRangeSetTestCase:
+    def setUp(self):
+        self._rs = self._CLS((self._range1, self._range2, self._range3))
+
+    def test_create(self):
+        self.assertEqual(len(self._rs), 3)
+        self.assertIn(self._range1, self._rs)
+        self.assertIn(self._range2, self._rs)
+        self.assertIn(self._range3, self._rs)
+
+    def test_create_tuples(self):
+        rs = self._CLS((
+            (self._range1.lower, self._range1.upper),
+            (self._range2.lower, self._range2.upper),
+            (self._range3.lower, self._range3.upper),
+        ))
+        self.assertEqual(len(rs), 3)
+        self.assertIn(self._range1, rs)
+        self.assertIn(self._range2, rs)
+        self.assertIn(self._range3, rs)
+
+    def test_create_non_iter(self):
+        with self.assertRaises(TypeError):
+            self._rs = self._CLS(23)
+
+    def test_create_wrong_elem_type(self):
+        with self.assertRaises(TypeError):
+            self._rs = self._CLS((self._range1, self._range2, 17))
+
+    def test_len(self):
+        self.assertEqual(len(self._rs), 3)
+
+    def test_contains(self):
+        rs = self._CLS((self._range1, self._range2))
+        self.assertIn(self._range1, rs)
+        self.assertIn(self._range2, rs)
+        self.assertNotIn(self._range3, rs)
+
+    def test_contains_value(self):
+        rs = self._CLS((self._range1, self._range2))
+        self.assertTrue(rs.contains_value(self._range1.upper))
+        self.assertTrue(rs.contains_value(self._range2.lower))
+        self.assertFalse(rs.contains_value(self._range3.upper))
+
+    def test_contains_value_wrong_type(self):
+        with self.assertRaises(TypeError):
+            self._rs.contains_value('meow')
+
+    def test_iter(self):
+        range_list = list(self._rs)
+        self.assertIn(self._range1, range_list)
+        self.assertIn(self._range2, range_list)
+        self.assertIn(self._range3, range_list)
+
+    def test_empty(self):
+        rs = self._CLS()
+        self.assertEqual(len(rs), 0)
+        self.assertEqual(len(list(rs)), 0)
+
+    def test_add_range_obj(self):
+        rs = self._CLS((self._range1,))
+        self.assertEqual(len(rs), 1)
+        rs.add(self._range2)
+        self.assertEqual(len(rs), 2)
+        self.assertIn(self._range2, rs)
+
+    def test_discard_not_implemented(self):
+        with self.assertRaises(NotImplementedError):
+            self._rs.discard(self._range2)
+
+    def test_eq_same_order(self):
+        rs = self._CLS((self._range1, self._range2, self._range3))
+        self.assertEqual(self._rs, rs)
+
+    def test_eq_diff_order(self):
+        rs = self._CLS((self._range1, self._range3, self._range2))
+        self.assertEqual(self._rs, rs)
+
+    def test_eq_same_addr(self):
+        self.assertEqual(self._rs, self._rs)
+
+    def test_ne_diff_len(self):
+        rs = self._CLS((self._range1, self._range2))
+        self.assertNotEqual(self._rs, rs)
+
+    def test_ne_diff_values(self):
+        rs1 = self._CLS((self._range1, self._range2))
+        rs2 = self._CLS((self._range1, self._range3))
+        self.assertNotEqual(rs1, rs2)
+
+    def test_ne_incompatible_type(self):
+        self.assertNotEqual(self._rs, object())
+
+
+class UnsignedIntegerRangeSetTestCase(_IntegerRangeSetTestCase, unittest.TestCase):
+    _CLS = bt2.UnsignedIntegerRangeSet
+
+    def setUp(self):
+        self._range1 = bt2.UnsignedIntegerRange(4, 192)
+        self._range2 = bt2.UnsignedIntegerRange(17, 228)
+        self._range3 = bt2.UnsignedIntegerRange(1000, 2000)
+        super().setUp()
+
+
+class SignedIntegerRangeSetTestCase(_IntegerRangeSetTestCase, unittest.TestCase):
+    _CLS = bt2.SignedIntegerRangeSet
+
+    def setUp(self):
+        self._range1 = bt2.SignedIntegerRange(-1484, -17)
+        self._range2 = bt2.SignedIntegerRange(-101, 1500)
+        self._range3 = bt2.SignedIntegerRange(1948, 2019)
+        super().setUp()
This page took 0.031216 seconds and 4 git commands to generate.