From ead8c3d49f4ee6b8b826062bd88b943a23f5faf0 Mon Sep 17 00:00:00 2001 From: Philippe Proulx Date: Tue, 13 Aug 2019 19:35:12 -0400 Subject: [PATCH] bt2: add bit array field class and field support This patch adds bit array field class and field support to the Python bindings. A bit array field class has a `length` property (number of bits in the array). As of this patch, a bit array field is not a Python sequence. It could become in the future however. The way to access a bit array field's value is the `value_as_integer` property which simply wraps the corresponding library functions. A bit array field is not considered a numeric field. Its _spec_eq() method only compares if the other object is also a bit array fields. I did not implement the __bool__() operator because it will need to satisfy the Python sequence protocol (length is not 0, which in fact is always the case with a bit array field), should we decide as such in the future. I implemented the __len__() operator however which is straightforward. Signed-off-by: Philippe Proulx Change-Id: I2e90e1444151bf5169a8df70c53664096cceb6c4 Reviewed-on: https://review.lttng.org/c/babeltrace/+/1918 Tested-by: jenkins --- src/bindings/python/bt2/bt2/__init__.py | 2 + src/bindings/python/bt2/bt2/field.py | 29 ++++++++++++ src/bindings/python/bt2/bt2/field_class.py | 11 +++++ src/bindings/python/bt2/bt2/trace_class.py | 15 ++++++ tests/bindings/python/bt2/test_field.py | 46 +++++++++++++++++++ tests/bindings/python/bt2/test_field_class.py | 24 ++++++++++ tests/bindings/python/bt2/test_package.py | 6 +++ 7 files changed, 133 insertions(+) diff --git a/src/bindings/python/bt2/bt2/__init__.py b/src/bindings/python/bt2/bt2/__init__.py index 410ad445..137fc423 100644 --- a/src/bindings/python/bt2/bt2/__init__.py +++ b/src/bindings/python/bt2/bt2/__init__.py @@ -42,6 +42,7 @@ from bt2.error import _MessageIteratorErrorCause from bt2.error import _Error from bt2.event_class import EventClassLogLevel from bt2.field import _BoolField +from bt2.field import _BitArrayField from bt2.field import _IntegerField from bt2.field import _UnsignedIntegerField from bt2.field import _SignedIntegerField @@ -58,6 +59,7 @@ from bt2.field import _StaticArrayField from bt2.field import _DynamicArrayField from bt2.field_class import IntegerDisplayBase from bt2.field_class import _BoolFieldClass +from bt2.field_class import _BitArrayFieldClass from bt2.field_class import _IntegerFieldClass from bt2.field_class import _UnsignedIntegerFieldClass from bt2.field_class import _SignedIntegerFieldClass diff --git a/src/bindings/python/bt2/bt2/field.py b/src/bindings/python/bt2/bt2/field.py index f674a465..512271bd 100644 --- a/src/bindings/python/bt2/bt2/field.py +++ b/src/bindings/python/bt2/bt2/field.py @@ -71,6 +71,34 @@ class _Field(object._UniqueObject): return self._repr() +class _BitArrayField(_Field): + _NAME = 'Bit array' + + @property + def value_as_integer(self): + return native_bt.field_bit_array_get_value_as_integer(self._ptr) + + @value_as_integer.setter + def value_as_integer(self, value): + utils._check_uint64(value) + native_bt.field_bit_array_set_value_as_integer(self._ptr, value) + + def _spec_eq(self, other): + if type(other) is not type(self): + return False + + return self.value_as_integer == other.value_as_integer + + def _repr(self): + return repr(self.value_as_integer) + + def __str__(self): + return str(self.value_as_integer) + + def __len__(self): + return self.field_class.length + + @functools.total_ordering class _NumericField(_Field): @staticmethod @@ -670,6 +698,7 @@ class _DynamicArrayField(_ArrayField, _Field): _TYPE_ID_TO_OBJ = { native_bt.FIELD_CLASS_TYPE_BOOL: _BoolField, + native_bt.FIELD_CLASS_TYPE_BIT_ARRAY: _BitArrayField, native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerField, native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerField, native_bt.FIELD_CLASS_TYPE_REAL: _RealField, diff --git a/src/bindings/python/bt2/bt2/field_class.py b/src/bindings/python/bt2/bt2/field_class.py index 9974a292..75d7cc5a 100644 --- a/src/bindings/python/bt2/bt2/field_class.py +++ b/src/bindings/python/bt2/bt2/field_class.py @@ -54,6 +54,16 @@ class _BoolFieldClass(_FieldClass): _NAME = 'Boolean' +class _BitArrayFieldClass(_FieldClass): + _NAME = 'Bit array' + + @property + def length(self): + length = native_bt.field_class_bit_array_get_length(self._ptr) + assert length >= 1 + return length + + class _IntegerFieldClass(_FieldClass): @property def field_value_range(self): @@ -603,6 +613,7 @@ class _DynamicArrayFieldClass(_ArrayFieldClass): _FIELD_CLASS_TYPE_TO_OBJ = { native_bt.FIELD_CLASS_TYPE_BOOL: _BoolFieldClass, + native_bt.FIELD_CLASS_TYPE_BIT_ARRAY: _BitArrayFieldClass, native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerFieldClass, native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerFieldClass, native_bt.FIELD_CLASS_TYPE_REAL: _RealFieldClass, diff --git a/src/bindings/python/bt2/bt2/trace_class.py b/src/bindings/python/bt2/bt2/trace_class.py index 3ae3adde..b9ac172f 100644 --- a/src/bindings/python/bt2/bt2/trace_class.py +++ b/src/bindings/python/bt2/bt2/trace_class.py @@ -194,6 +194,21 @@ class _TraceClass(object._SharedObject, collections.abc.Mapping): return bt2_field_class._BoolFieldClass._create_from_ptr(field_class_ptr) + def create_bit_array_field_class(self, length): + utils._check_uint64(length) + + if length < 1 or length > 64: + raise ValueError( + 'invalid length {}: expecting a value in the [1, 64] range'.format( + length + ) + ) + + field_class_ptr = native_bt.field_class_bit_array_create(self._ptr, length) + self._check_field_class_create_status(field_class_ptr, 'bit array') + + return bt2_field_class._BitArrayFieldClass._create_from_ptr(field_class_ptr) + def _create_integer_field_class( self, create_func, py_cls, type_name, field_value_range, preferred_display_base ): diff --git a/tests/bindings/python/bt2/test_field.py b/tests/bindings/python/bt2/test_field.py index 429fe2ee..5c8d8287 100644 --- a/tests/bindings/python/bt2/test_field.py +++ b/tests/bindings/python/bt2/test_field.py @@ -126,6 +126,52 @@ def _create_struct_array_field(tc, length): return packet.context_field[field_name] +class BitArrayFieldTestCase(unittest.TestCase): + def _create_field(self): + return _create_field(self._tc, self._tc.create_bit_array_field_class(24)) + + def setUp(self): + self._tc = get_default_trace_class() + self._def_value = 15497 + self._def = self._create_field() + self._def.value_as_integer = self._def_value + self._def_new_value = 101542 + + def test_assign_invalid_type(self): + with self.assertRaises(TypeError): + self._def.value_as_integer = 'onze' + + def test_assign(self): + self._def.value_as_integer = 199 + self.assertEqual(self._def.value_as_integer, 199) + + def test_assign_masked(self): + self._def.value_as_integer = 0xE1549BB + self.assertEqual(self._def.value_as_integer, 0xE1549BB & ((1 << 24) - 1)) + + def test_eq(self): + other = self._create_field() + other.value_as_integer = self._def_value + self.assertEqual(self._def, other) + + def test_ne_same_type(self): + other = self._create_field() + other.value_as_integer = self._def_value - 1 + self.assertNotEqual(self._def, other) + + def test_ne_diff_type(self): + self.assertNotEqual(self._def, self._def_value) + + def test_len(self): + self.assertEqual(len(self._def), 24) + + def test_str(self): + self.assertEqual(str(self._def), str(self._def_value)) + + def test_repr(self): + self.assertEqual(repr(self._def), repr(self._def_value)) + + # Base class for numeric field test cases. # # To be compatible with this base class, a derived class must, in its diff --git a/tests/bindings/python/bt2/test_field_class.py b/tests/bindings/python/bt2/test_field_class.py index af956507..e564cac5 100644 --- a/tests/bindings/python/bt2/test_field_class.py +++ b/tests/bindings/python/bt2/test_field_class.py @@ -30,6 +30,30 @@ class BoolFieldClassTestCase(unittest.TestCase): self.assertIsNotNone(self._fc) +class BitArrayFieldClassTestCase(unittest.TestCase): + def setUp(self): + self._tc = get_default_trace_class() + self._fc = self._tc.create_bit_array_field_class(17) + + def test_create_default(self): + self.assertIsNotNone(self._fc) + + def test_create_length_out_of_range(self): + with self.assertRaises(ValueError): + self._tc.create_bit_array_field_class(65) + + def test_create_length_zero(self): + with self.assertRaises(ValueError): + self._tc.create_bit_array_field_class(0) + + def test_create_length_invalid_type(self): + with self.assertRaises(TypeError): + self._tc.create_bit_array_field_class('lel') + + def test_length_prop(self): + self.assertEqual(self._fc.length, 17) + + class _TestIntegerFieldClassProps: def test_create_default(self): fc = self._create_func() diff --git a/tests/bindings/python/bt2/test_package.py b/tests/bindings/python/bt2/test_package.py index bcf30fed..71e792a4 100644 --- a/tests/bindings/python/bt2/test_package.py +++ b/tests/bindings/python/bt2/test_package.py @@ -84,6 +84,9 @@ class PackageTestCase(unittest.TestCase): def test_has__BoolField(self): self._assert_in_bt2('_BoolField') + def test_has__BitArrayField(self): + self._assert_in_bt2('_BitArrayField') + def test_has__IntegerField(self): self._assert_in_bt2('_IntegerField') @@ -132,6 +135,9 @@ class PackageTestCase(unittest.TestCase): def test_has__BoolFieldClass(self): self._assert_in_bt2('_BoolFieldClass') + def test_has__BitArrayFieldClass(self): + self._assert_in_bt2('_BitArrayFieldClass') + def test_has__IntegerFieldClass(self): self._assert_in_bt2('_IntegerFieldClass') -- 2.34.1