bt2: Adapt test_field_class.py and make it pass
authorSimon Marchi <simon.marchi@efficios.com>
Wed, 29 May 2019 16:08:29 +0000 (12:08 -0400)
committerPhilippe Proulx <eeppeliteloop@gmail.com>
Wed, 5 Jun 2019 17:47:34 +0000 (13:47 -0400)
Update the test_field_class.py test to match the current API of
Babeltrace.  Update field_class.py and some others to make that test
pass.

The main change in test_field_class.py is that field classes are now
created from an existing trace class (tc.create_foo_field_class())
rather than by constructor (FooFieldClass()).  Everything related to
copy, deep copy and equality is removed.  Everything related to
structure alignment, byte order and encoding is removed, as those were
ctf concepts that were removed from Babeltrace's trace-ir API.

All specific field class tests try to follow the same pattern.  The
test_create_default test method verifies the properties of a specific
field class create with passing as few parameters as possible.  Then,
for each possible creation parameter, we verify a case that works and
some problematic cases such as invalid value or invalid type.  More
tests are done for specific field classes that support additional
features, such as container field classes.

The support for modifying field class properties after they have been
created is removed.  All characteristics of a field class must passed
during construction (this doesn't apply to fields of composite field
classes).

A new concept of "field path" was introduced in the library, which is
useful in the context of dynamically-sized arrays and variants.  Support
is therefore added to obtain the "field path" to a variant's selector
field or dynamic array length field.

Change-Id: Ia998119fcf7c61ef5904fbe72baa36a2838d5780
Signed-off-by: Simon Marchi <simon.marchi@efficios.com>
Signed-off-by: Francis Deslauriers <francis.deslauriers@efficios.com>
Reviewed-on: https://review.lttng.org/c/babeltrace/+/1319
Tested-by: jenkins
Reviewed-by: Philippe Proulx <eeppeliteloop@gmail.com>
bindings/python/bt2/Makefile.am
bindings/python/bt2/bt2/__init__.py.in
bindings/python/bt2/bt2/field_class.py
bindings/python/bt2/bt2/field_path.py [new file with mode: 0644]
bindings/python/bt2/bt2/trace_class.py
tests/bindings/python/bt2/test_event_class.py
tests/bindings/python/bt2/test_field_class.py

index 90ee322e2fac8c20d0224ac1bd075d7fd4f54825..44e96441116d124633e17b3838fff19f577b81a3 100644 (file)
@@ -42,6 +42,7 @@ STATIC_BINDINGS_DEPS =                                        \
        bt2/event.py                                    \
        bt2/field.py                                    \
        bt2/field_class.py                              \
+       bt2/field_path.py                               \
        bt2/graph.py                                    \
        bt2/logging.py                                  \
        bt2/message_iterator.py                         \
index f8fd8482086967ba3370d25e4c183e05aa2cd2f1..08fc0cacbaaa0706cb136e2969926847f6d5454a 100644 (file)
@@ -42,7 +42,7 @@ from bt2.ctf_writer import _CtfWriterStream
 from bt2.event import _Event
 from bt2.event_class import *
 from bt2.field_class import *
-from bt2.field_class import _FieldClass
+from bt2.field_path import *
 from bt2.field import *
 from bt2.field import _ArrayField
 from bt2.field import _EnumerationField
index 35dfcdec3f78b8b2451441f91ac3595cca881609..27a37d96baeccedebc9c7de81c9ef2d0d84a6299 100644 (file)
@@ -23,7 +23,7 @@
 from bt2 import native_bt, object, utils
 import collections.abc
 import bt2.field
-import abc
+import bt2.field_path
 import bt2
 
 
@@ -32,245 +32,86 @@ def _create_field_class_from_ptr_and_get_ref(ptr):
     return _FIELD_CLASS_TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr)
 
 
-class _FieldClass(object._SharedObject, metaclass=abc.ABCMeta):
-    _get_ref = staticmethod(native_bt.field_class_get_ref)
-    _put_ref = staticmethod(native_bt.field_class_put_ref)
-
-    def __init__(self, ptr):
-        super().__init__(ptr)
-
-    def __eq__(self, other):
-        if not isinstance(other, self.__class__):
-            # not comparing apples to apples
-            return False
+class IntegerDisplayBase:
+    BINARY = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_BINARY
+    OCTAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_OCTAL
+    DECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_DECIMAL
+    HEXADECIMAL = native_bt.FIELD_CLASS_INTEGER_PREFERRED_DISPLAY_BASE_HEXADECIMAL
 
-        if self.addr == other.addr:
-            return True
 
-        ret = native_bt.field_class_compare(self._ptr, other._ptr)
-        utils._handle_ret(ret, "cannot compare field classes")
-        return ret == 0
+class _FieldClass(object._SharedObject):
+    _get_ref = staticmethod(native_bt.field_class_get_ref)
+    _put_ref = staticmethod(native_bt.field_class_put_ref)
 
     def _check_create_status(self, ptr):
         if ptr is None:
             raise bt2.CreationError('cannot create {} field class object'.format(self._NAME.lower()))
 
-    def __copy__(self):
-        ptr = native_bt.field_class_copy(self._ptr)
-        utils._handle_ptr(ptr, 'cannot copy {} field class object'.format(self._NAME.lower()))
-        return _create_from_ptr(ptr)
-
-    def __deepcopy__(self, memo):
-        cpy = self.__copy__()
-        memo[id(self)] = cpy
-        return cpy
-
-    def __call__(self, value=None):
-        field_ptr = native_bt.field_create(self._ptr)
-
-        if field_ptr is None:
-            raise bt2.CreationError('cannot create {} field object'.format(self._NAME.lower()))
-
-        field = bt2.field._create_from_ptr(field_ptr)
-
-        if value is not None:
-            if not isinstance(field, (bt2.field._IntegerField, bt2.field._FloatingPointNumberField, bt2.field._StringField)):
-                raise bt2.Error('cannot assign an initial value to a {} field object'.format(field._NAME))
-
-            field.value = value
-
-        return field
-
-
-class _AlignmentProp:
-    @property
-    def alignment(self):
-        alignment = native_bt.field_class_get_alignment(self._ptr)
-        assert(alignment >= 0)
-        return alignment
-
-    @alignment.setter
-    def alignment(self, alignment):
-        utils._check_alignment(alignment)
-        ret = native_bt.field_class_set_alignment(self._ptr, alignment)
-        utils._handle_ret(ret, "cannot set field class object's alignment")
-
-
-class _ByteOrderProp:
-    @property
-    def byte_order(self):
-        bo = native_bt.field_class_get_byte_order(self._ptr)
-        assert(bo >= 0)
-        return bo
-
-    @byte_order.setter
-    def byte_order(self, byte_order):
-        utils._check_int(byte_order)
-        ret = native_bt.field_class_set_byte_order(self._ptr, byte_order)
-        utils._handle_ret(ret, "cannot set field class object's byte order")
-
 
 class _IntegerFieldClass(_FieldClass):
-
-    def __init__(self, size, alignment=None, byte_order=None, is_signed=None,
-                 base=None, encoding=None, mapped_clock_class=None):
-        utils._check_uint64(size)
-
-        if size == 0:
-            raise ValueError('size is 0 bits')
-
-        ptr = native_bt.field_class_integer_create(size)
-        self._check_create_status(ptr)
-        super().__init__(ptr)
-
-        if alignment is not None:
-            self.alignment = alignment
-
-        if byte_order is not None:
-            self.byte_order = byte_order
-
-        if is_signed is not None:
-            self.is_signed = is_signed
-
-        if base is not None:
-            self.base = base
-
-        if encoding is not None:
-            self.encoding = encoding
-
-        if mapped_clock_class is not None:
-            self.mapped_clock_class = mapped_clock_class
-
     @property
-    def size(self):
-        size = native_bt.field_class_integer_get_size(self._ptr)
+    def field_value_range(self):
+        size = native_bt.field_class_integer_get_field_value_range(self._ptr)
         assert(size >= 1)
         return size
 
-    @property
-    def is_signed(self):
-        is_signed = native_bt.field_class_integer_is_signed(self._ptr)
-        assert(is_signed >= 0)
-        return is_signed > 0
+    def _field_value_range(self, size):
+        if size < 1 or size > 64:
+            raise ValueError("Value is outside valid range [1, 64] ({})".format(size))
+        native_bt.field_class_integer_set_field_value_range(self._ptr, size)
 
-    @is_signed.setter
-    def is_signed(self, is_signed):
-        utils._check_bool(is_signed)
-        ret = native_bt.field_class_integer_set_is_signed(self._ptr, int(is_signed))
-        utils._handle_ret(ret, "cannot set integer field class object's signedness")
+    _field_value_range = property(fset=_field_value_range)
 
     @property
-    def base(self):
-        base = native_bt.field_class_integer_get_base(self._ptr)
+    def preferred_display_base(self):
+        base = native_bt.field_class_integer_get_preferred_display_base(
+            self._ptr)
         assert(base >= 0)
         return base
 
-    @base.setter
-    def base(self, base):
-        utils._check_int(base)
-        ret = native_bt.field_class_integer_set_base(self._ptr, base)
-        utils._handle_ret(ret, "cannot set integer field class object's base")
+    def _preferred_display_base(self, base):
+        utils._check_uint64(base)
 
-    @property
-    def encoding(self):
-        encoding = native_bt.field_class_integer_get_encoding(self._ptr)
-        assert(encoding >= 0)
-        return encoding
+        if base not in (IntegerDisplayBase.BINARY,
+                        IntegerDisplayBase.OCTAL,
+                        IntegerDisplayBase.DECIMAL,
+                        IntegerDisplayBase.HEXADECIMAL):
+            raise ValueError("Display base is not a valid IntegerDisplayBase value")
 
-    @encoding.setter
-    def encoding(self, encoding):
-        utils._check_int(encoding)
-        ret = native_bt.field_class_integer_set_encoding(self._ptr, encoding)
-        utils._handle_ret(ret, "cannot set integer field class object's encoding")
+        native_bt.field_class_integer_set_preferred_display_base(
+            self._ptr, base)
 
-    @property
-    def mapped_clock_class(self):
-        ptr = native_bt.field_class_integer_get_mapped_clock_class(self._ptr)
-
-        if ptr is None:
-            return
-
-        return bt2.ClockClass._create_from_ptr(ptr)
-
-    @mapped_clock_class.setter
-    def mapped_clock_class(self, clock_class):
-        utils._check_type(clock_class, bt2.ClockClass)
-        ret = native_bt.field_class_integer_set_mapped_clock_class(self._ptr, clock_class._ptr)
-        utils._handle_ret(ret, "cannot set integer field class object's mapped clock class")
+    _preferred_display_base = property(fset=_preferred_display_base)
 
 
 class _UnsignedIntegerFieldClass(_IntegerFieldClass):
-    pass
+    _NAME = 'Unsigned integer'
 
 
 class _SignedIntegerFieldClass(_IntegerFieldClass):
-    pass
-
-
-class UnsignedIntegerFieldClass(_UnsignedIntegerFieldClass):
-    _NAME = 'UnsignedInteger'
-
+    _NAME = 'Signed integer'
 
-class SignedIntegerFieldClass(_SignedIntegerFieldClass):
-    _NAME = 'SignedInteger'
 
-
-class RealFieldClass(_FieldClass):
+class _RealFieldClass(_FieldClass):
     _NAME = 'Real'
 
-    def __init__(self, alignment=None, byte_order=None, exponent_size=None,
-                 mantissa_size=None):
-        ptr = native_bt.field_class_floating_point_create()
-        self._check_create_status(ptr)
-        super().__init__(ptr)
-
-        if alignment is not None:
-            self.alignment = alignment
-
-        if byte_order is not None:
-            self.byte_order = byte_order
-
-        if exponent_size is not None:
-            self.exponent_size = exponent_size
-
-        if mantissa_size is not None:
-            self.mantissa_size = mantissa_size
-
     @property
-    def exponent_size(self):
-        exp_size = native_bt.field_class_floating_point_get_exponent_digits(self._ptr)
-        assert(exp_size >= 0)
-        return exp_size
+    def is_single_precision(self):
+        return native_bt.field_class_real_is_single_precision(self._ptr)
 
-    @exponent_size.setter
-    def exponent_size(self, exponent_size):
-        utils._check_uint64(exponent_size)
-        ret = native_bt.field_class_floating_point_set_exponent_digits(self._ptr, exponent_size)
-        utils._handle_ret(ret, "cannot set floating point number field class object's exponent size")
-
-    @property
-    def mantissa_size(self):
-        mant_size = native_bt.field_class_floating_point_get_mantissa_digits(self._ptr)
-        assert(mant_size >= 0)
-        return mant_size
+    def _is_single_precision(self, is_single_precision):
+        utils._check_bool(is_single_precision)
+        native_bt.field_class_real_set_is_single_precision(
+            self._ptr, is_single_precision)
 
-    @mantissa_size.setter
-    def mantissa_size(self, mantissa_size):
-        utils._check_uint64(mantissa_size)
-        ret = native_bt.field_class_floating_point_set_mantissa_digits(self._ptr, mantissa_size)
-        utils._handle_ret(ret, "cannot set floating point number field class object's mantissa size")
+    _is_single_precision = property(fset=_is_single_precision)
 
 
-class _EnumerationFieldClassMapping:
-    def __init__(self, name, lower, upper):
-        self._name = name
+class _EnumerationFieldClassMappingRange:
+    def __init__(self, lower, upper):
         self._lower = lower
         self._upper = upper
 
-    @property
-    def name(self):
-        return self._name
-
     @property
     def lower(self):
         return self._lower
@@ -280,413 +121,277 @@ class _EnumerationFieldClassMapping:
         return self._upper
 
     def __eq__(self, other):
-        if type(other) is not self.__class__:
-            return False
-
-        return (self.name, self.lower, self.upper) == (other.name, other.lower, other.upper)
-
-
-class _EnumerationFieldClassMappingIterator(object._SharedObject,
-                                            collections.abc.Iterator):
-    def __init__(self, iter_ptr, is_signed):
-        super().__init__(iter_ptr)
-        self._is_signed = is_signed
-        self._done = (iter_ptr is None)
-
-    def __next__(self):
-        if self._done:
-            raise StopIteration
-
-        ret = native_bt.field_class_enumeration_mapping_iterator_next(self._ptr)
-        if ret < 0:
-            self._done = True
-            raise StopIteration
-
-        if self._is_signed:
-            ret, name, lower, upper = native_bt.field_class_enumeration_mapping_iterator_get_signed(self._ptr)
-        else:
-            ret, name, lower, upper = native_bt.field_class_enumeration_mapping_iterator_get_unsigned(self._ptr)
-
-        assert(ret == 0)
-        mapping = _EnumerationFieldClassMapping(name, lower, upper)
-
-        return mapping
-
-
-class EnumerationFieldClass(_IntegerFieldClass, collections.abc.Sequence):
-    _NAME = 'Enumeration'
-
-    def __init__(self, int_field_class=None, size=None, alignment=None,
-                 byte_order=None, is_signed=None, base=None, encoding=None,
-                 mapped_clock_class=None):
-        if int_field_class is None:
-            int_field_class = IntegerFieldClass(size=size, alignment=alignment,
-                                              byte_order=byte_order,
-                                              is_signed=is_signed, base=base,
-                                              encoding=encoding,
-                                              mapped_clock_class=mapped_clock_class)
-
-        utils._check_type(int_field_class, IntegerFieldClass)
-        ptr = native_bt.field_class_enumeration_create(int_field_class._ptr)
-        self._check_create_status(ptr)
-        _FieldClass.__init__(self, ptr)
-
-    @property
-    def integer_field_class(self):
-        ptr = native_bt.field_class_enumeration_get_container_type(self._ptr)
-        assert(ptr)
-        return _create_from_ptr(ptr)
-
-    @property
-    def size(self):
-        return self.integer_field_class.size
+        return self.lower == other.lower and self.upper == other.upper
 
-    @property
-    def alignment(self):
-        return self.integer_field_class.alignment
 
-    @alignment.setter
-    def alignment(self, alignment):
-        self.integer_field_class.alignment = alignment
+class _EnumerationFieldClassMapping(collections.abc.Set):
+    def __init__(self, mapping_ptr):
+        self._mapping_ptr = mapping_ptr
 
     @property
-    def byte_order(self):
-        return self.integer_field_class.byte_order
-
-    @byte_order.setter
-    def byte_order(self, byte_order):
-        self.integer_field_class.byte_order = byte_order
+    def label(self):
+        mapping_ptr = self._as_enumeration_field_class_mapping_ptr(self._mapping_ptr)
+        label = native_bt.field_class_enumeration_mapping_get_label(mapping_ptr)
+        assert label is not None
+        return label
 
-    @property
-    def is_signed(self):
-        return self.integer_field_class.is_signed
+    def __len__(self):
+        mapping_ptr = self._as_enumeration_field_class_mapping_ptr(self._mapping_ptr)
+        return native_bt.field_class_enumeration_mapping_get_range_count(mapping_ptr)
 
-    @is_signed.setter
-    def is_signed(self, is_signed):
-        self.integer_field_class.is_signed = is_signed
+    def __contains__(self, other_range):
+        for curr_range in self:
+            if curr_range == other_range:
+                return True
+        return False
 
-    @property
-    def base(self):
-        return self.integer_field_class.base
+    def __iter__(self):
+        for idx in range(len(self)):
+            lower, upper = self._get_range_by_index(self._mapping_ptr, idx)
+            yield _EnumerationFieldClassMappingRange(lower, upper)
 
-    @base.setter
-    def base(self, base):
-        self.integer_field_class.base = base
 
-    @property
-    def encoding(self):
-        return self.integer_field_class.encoding
+class _UnsignedEnumerationFieldClassMapping(_EnumerationFieldClassMapping):
+    _as_enumeration_field_class_mapping_ptr = staticmethod(native_bt.field_class_unsigned_enumeration_mapping_as_mapping_const)
+    _get_range_by_index = staticmethod(native_bt.field_class_unsigned_enumeration_mapping_get_range_by_index)
 
-    @encoding.setter
-    def encoding(self, encoding):
-        self.integer_field_class.encoding = encoding
 
-    @property
-    def mapped_clock_class(self):
-        return self.integer_field_class.mapped_clock_class
+class _SignedEnumerationFieldClassMapping(_EnumerationFieldClassMapping):
+    _as_enumeration_field_class_mapping_ptr = staticmethod(native_bt.field_class_signed_enumeration_mapping_as_mapping_const)
+    _get_range_by_index = staticmethod(native_bt.field_class_signed_enumeration_mapping_get_range_by_index)
 
-    @mapped_clock_class.setter
-    def mapped_clock_class(self, mapped_clock_class):
-        self.integer_field_class.mapped_clock_class = mapped_clock_class
 
+class _EnumerationFieldClass(_IntegerFieldClass, collections.abc.Mapping):
     def __len__(self):
         count = native_bt.field_class_enumeration_get_mapping_count(self._ptr)
         assert(count >= 0)
         return count
 
-    def __getitem__(self, index):
-        utils._check_uint64(index)
+    def map_range(self, label, lower, upper=None):
+        utils._check_str(label)
 
-        if index >= len(self):
-            raise IndexError
+        if upper is None:
+            upper = lower
 
-        if self.is_signed:
-            get_fn = native_bt.field_class_enumeration_get_mapping_signed
-        else:
-            get_fn = native_bt.field_class_enumeration_get_mapping_unsigned
+        ret = self._map_range(self._ptr, label, lower, upper)
+        utils._handle_ret(ret, "cannot add mapping to enumeration field class object")
 
-        ret, name, lower, upper = get_fn(self._ptr, index)
-        assert(ret == 0)
-        return _EnumerationFieldClassMapping(name, lower, upper)
+    def labels_by_value(self, value):
+        ret, labels = self._get_mapping_labels_by_value(self._ptr, value)
+        utils._handle_ret(ret, "cannot get mapping labels")
+        return labels
 
-    def _get_mapping_iter(self, iter_ptr):
-        return _EnumerationFieldClassMappingIterator(iter_ptr, self.is_signed)
+    def __iter__(self):
+        for idx in range(len(self)):
+            mapping = self._get_mapping_by_index(self._ptr, idx)
+            yield mapping.label
 
-    def mappings_by_name(self, name):
-        utils._check_str(name)
-        iter_ptr = native_bt.field_class_enumeration_find_mappings_by_name(self._ptr, name)
-        print('iter_ptr', iter_ptr)
-        return self._get_mapping_iter(iter_ptr)
+    def __getitem__(self, key):
+        utils._check_str(key)
+        for idx in range(len(self)):
+            mapping = self._get_mapping_by_index(self._ptr, idx)
+            if mapping.label == key:
+                return mapping
 
-    def mappings_by_value(self, value):
-        if self.is_signed:
-            utils._check_int64(value)
-            iter_ptr = native_bt.field_class_enumeration_find_mappings_by_signed_value(self._ptr, value)
-        else:
-            utils._check_uint64(value)
-            iter_ptr = native_bt.field_class_enumeration_find_mappings_by_unsigned_value(self._ptr, value)
+        raise KeyError(key)
 
-        return self._get_mapping_iter(iter_ptr)
+    def __iadd__(self, mappings):
+        for mapping in mappings.values():
+            for range in mapping:
+                self.map_range(mapping.label, range.lower, range.upper)
 
-    def add_mapping(self, name, lower, upper=None):
-        utils._check_str(name)
+        return self
 
-        if upper is None:
-            upper = lower
 
-        if self.is_signed:
-            add_fn = native_bt.field_class_enumeration_add_mapping_signed
-            utils._check_int64(lower)
-            utils._check_int64(upper)
-        else:
-            add_fn = native_bt.field_class_enumeration_add_mapping_unsigned
-            utils._check_uint64(lower)
-            utils._check_uint64(upper)
+class _UnsignedEnumerationFieldClass(_EnumerationFieldClass, _UnsignedIntegerFieldClass):
+    _NAME = 'Unsigned enumeration'
 
-        ret = add_fn(self._ptr, name, lower, upper)
-        utils._handle_ret(ret, "cannot add mapping to enumeration field class object")
+    @staticmethod
+    def _get_mapping_by_index(enum_ptr, index):
+        mapping_ptr = native_bt.field_class_unsigned_enumeration_borrow_mapping_by_index_const(enum_ptr, index)
+        assert mapping_ptr is not None
+        return _UnsignedEnumerationFieldClassMapping(mapping_ptr)
 
-    def __iadd__(self, mappings):
-        for mapping in mappings:
-            self.add_mapping(mapping.name, mapping.lower, mapping.upper)
+    @staticmethod
+    def _map_range(enum_ptr, label, lower, upper):
+        utils._check_uint64(lower)
+        utils._check_uint64(upper)
+        return native_bt.field_class_unsigned_enumeration_map_range(enum_ptr, label, lower, upper)
 
-        return self
+    @staticmethod
+    def _get_mapping_labels_by_value(enum_ptr, value):
+        utils._check_uint64(value)
+        return native_bt.field_class_unsigned_enumeration_get_mapping_labels_by_value(enum_ptr, value)
 
 
-class StringFieldClass(_FieldClass):
-    _NAME = 'String'
+class _SignedEnumerationFieldClass(_EnumerationFieldClass, _SignedIntegerFieldClass):
+    _NAME = 'Signed enumeration'
 
-    def __init__(self, encoding=None):
-        ptr = native_bt.field_class_string_create()
-        self._check_create_status(ptr)
-        super().__init__(ptr)
+    @staticmethod
+    def _get_mapping_by_index(enum_ptr, index):
+        mapping_ptr = native_bt.field_class_signed_enumeration_borrow_mapping_by_index_const(enum_ptr, index)
+        assert mapping_ptr is not None
+        return _SignedEnumerationFieldClassMapping(mapping_ptr)
 
-        if encoding is not None:
-            self.encoding = encoding
+    @staticmethod
+    def _map_range(enum_ptr, label, lower, upper):
+        utils._check_int64(lower)
+        utils._check_int64(upper)
+        return native_bt.field_class_signed_enumeration_map_range(enum_ptr, label, lower, upper)
 
-    @property
-    def encoding(self):
-        encoding = native_bt.field_class_string_get_encoding(self._ptr)
-        assert(encoding >= 0)
-        return encoding
+    @staticmethod
+    def _get_mapping_labels_by_value(enum_ptr, value):
+        utils._check_int64(value)
+        return native_bt.field_class_signed_enumeration_get_mapping_labels_by_value(enum_ptr, value)
 
-    @encoding.setter
-    def encoding(self, encoding):
-        utils._check_int(encoding)
-        ret = native_bt.field_class_string_set_encoding(self._ptr, encoding)
-        utils._handle_ret(ret, "cannot set string field class object's encoding")
+
+class _StringFieldClass(_FieldClass):
+    _NAME = 'String'
 
 
 class _FieldContainer(collections.abc.Mapping):
     def __len__(self):
-        count = self._count()
-        assert(count >= 0)
+        count = self._get_element_count(self._ptr)
+        assert count >= 0
         return count
 
     def __getitem__(self, key):
         if not isinstance(key, str):
-            raise TypeError("'{}' is not a 'str' object".format(key.__class__.__name__))
+            raise TypeError("key should be a 'str' object, got {}".format(key.__class__.__name__))
 
-        ptr = self._get_field_by_name(key)
+        ptr = self._borrow_field_class_ptr_by_name(key)
 
         if ptr is None:
             raise KeyError(key)
 
-        return _create_from_ptr(ptr)
+        return _create_field_class_from_ptr_and_get_ref(ptr)
+
+    def _borrow_field_class_ptr_by_name(self, key):
+        element_ptr = self._borrow_element_by_name(self._ptr, key)
+        if element_ptr is None:
+            return
+
+        return self._element_borrow_field_class(element_ptr)
 
     def __iter__(self):
-        return self._ITER_CLS(self)
+        for idx in range(len(self)):
+            element_ptr = self._borrow_element_by_index(self._ptr, idx)
+            assert element_ptr is not None
 
-    def append_field(self, name, field_class):
+            yield self._element_get_name(element_ptr)
+
+    def _append_element_common(self, name, field_class):
         utils._check_str(name)
         utils._check_type(field_class, _FieldClass)
-        ret = self._add_field(name, field_class._ptr)
+        ret = self._append_element(self._ptr, name, field_class._ptr)
         utils._handle_ret(ret, "cannot add field to {} field class object".format(self._NAME.lower()))
 
     def __iadd__(self, fields):
         for name, field_class in fields.items():
-            self.append_field(name, field_class)
+            self._append_element_common(name, field_class)
 
         return self
 
-    def at_index(self, index):
+    def _at_index(self, index):
         utils._check_uint64(index)
-        return self._at(index)
-
 
-class _StructureFieldClassFieldIterator(collections.abc.Iterator):
-    def __init__(self, struct_field_class):
-        self._struct_field_class = struct_field_class
-        self._at = 0
-
-    def __next__(self):
-        if self._at == len(self._struct_field_class):
-            raise StopIteration
-
-        get_fc_by_index = native_bt.field_class_structure_get_field_by_index
-        ret, name, field_class_ptr = get_fc_by_index(self._struct_field_class._ptr,
-                                                    self._at)
-        assert(ret == 0)
-        native_bt.put(field_class_ptr)
-        self._at += 1
-        return name
-
-
-class _StructureFieldClass(_FieldClass, _FieldContainer, _AlignmentProp):
-    _NAME = 'Structure'
-    _ITER_CLS = _StructureFieldClassFieldIterator
-
-    def __init__(self, min_alignment=None):
-        ptr = native_bt.field_class_structure_create()
-        self._check_create_status(ptr)
-        super().__init__(ptr)
-
-        if min_alignment is not None:
-            self.min_alignment = min_alignment
-
-    def _count(self):
-        return native_bt.field_class_structure_get_field_count(self._ptr)
-
-    def _get_field_by_name(self, key):
-        return native_bt.field_class_structure_get_field_class_by_name(self._ptr, key)
-
-    def _add_field(self, name, ptr):
-        return native_bt.field_class_structure_append_member(self._ptr, name, ptr)
-
-    def _at(self, index):
         if index < 0 or index >= len(self):
             raise IndexError
 
-        ret, name, field_class_ptr = native_bt.field_class_structure_get_field_by_index(self._ptr, index)
-        assert(ret == 0)
-        return _create_from_ptr(field_class_ptr)
+        element_ptr = self._borrow_element_by_index(self._ptr, index)
+        assert element_ptr is not None
 
+        field_class_ptr = self._element_borrow_field_class(element_ptr)
 
-_StructureFieldClass.min_alignment = property(fset=_StructureFieldClass.alignment.fset)
-_StructureFieldClass.alignment = property(fget=_StructureFieldClass.alignment.fget)
+        return _create_field_class_from_ptr_and_get_ref(field_class_ptr)
 
 
-class _VariantFieldClassFieldIterator(collections.abc.Iterator):
-    def __init__(self, variant_field_class):
-        self._variant_field_class = variant_field_class
-        self._at = 0
+class _StructureFieldClass(_FieldClass, _FieldContainer):
+    _NAME = 'Structure'
+    _borrow_element_by_index = staticmethod(native_bt.field_class_structure_borrow_member_by_index_const)
+    _borrow_element_by_name = staticmethod(native_bt.field_class_structure_borrow_member_by_name_const)
+    _element_get_name = staticmethod(native_bt.field_class_structure_member_get_name)
+    _element_borrow_field_class = staticmethod(native_bt.field_class_structure_member_borrow_field_class_const)
+    _get_element_count = staticmethod(native_bt.field_class_structure_get_member_count)
+    _append_element = staticmethod(native_bt.field_class_structure_append_member)
 
-    def __next__(self):
-        if self._at == len(self._variant_field_class):
-            raise StopIteration
+    def append_member(self, name, field_class):
+        return self._append_element_common(name, field_class)
 
-        ret, name, field_class_ptr = native_bt.field_class_variant_get_field_by_index(self._variant_field_class._ptr,
-                                                                                    self._at)
-        assert(ret == 0)
-        native_bt.put(field_class_ptr)
-        self._at += 1
-        return name
+    def member_at_index(self, index):
+        return self._at_index(index)
 
 
-class VariantFieldClass(_FieldClass, _FieldContainer, _AlignmentProp):
+class _VariantFieldClass(_FieldClass, _FieldContainer):
     _NAME = 'Variant'
-    _ITER_CLS = _VariantFieldClassFieldIterator
-
-    def __init__(self, tag_name, tag_field_class=None):
-        utils._check_str(tag_name)
+    _borrow_element_by_index = staticmethod(native_bt.field_class_variant_borrow_option_by_index_const)
+    _borrow_element_by_name = staticmethod(native_bt.field_class_variant_borrow_option_by_name_const)
+    _element_get_name = staticmethod(native_bt.field_class_variant_option_get_name)
+    _element_borrow_field_class = staticmethod(native_bt.field_class_variant_option_borrow_field_class_const)
+    _get_element_count = staticmethod(native_bt.field_class_variant_get_option_count)
+    _append_element = staticmethod(native_bt.field_class_variant_append_option)
 
-        if tag_field_class is None:
-            tag_fc_ptr = None
-        else:
-            utils._check_type(tag_field_class, EnumerationFieldClass)
-            tag_fc_ptr = tag_field_class._ptr
+    def append_option(self, name, field_class):
+        return self._append_element_common(name, field_class)
 
-        ptr = native_bt.field_class_variant_create(tag_fc_ptr,
-                                                  tag_name)
-        self._check_create_status(ptr)
-        super().__init__(ptr)
+    def option_at_index(self, index):
+        return self._at_index(index)
 
     @property
-    def tag_name(self):
-        tag_name = native_bt.field_class_variant_get_tag_name(self._ptr)
-        assert(tag_name is not None)
-        return tag_name
-
-    @tag_name.setter
-    def tag_name(self, tag_name):
-        utils._check_str(tag_name)
-        ret = native_bt.field_class_variant_set_tag_name(self._ptr, tag_name)
-        utils._handle_ret(ret, "cannot set variant field class object's tag name")
-
-    @property
-    def tag_field_class(self):
-        fc_ptr = native_bt.field_class_variant_get_tag_type(self._ptr)
-
-        if fc_ptr is None:
+    def selector_field_path(self):
+        ptr = native_bt.field_class_variant_borrow_selector_field_path_const(self._ptr)
+        if ptr is None:
             return
 
-        return _create_from_ptr(fc_ptr)
-
-    def _count(self):
-        return native_bt.field_class_variant_get_field_count(self._ptr)
-
-    def _get_field_by_name(self, key):
-        return native_bt.field_class_variant_get_field_class_by_name(self._ptr, key)
+        return bt2.field_path._FieldPath._create_from_ptr_and_get_ref(ptr)
 
-    def _add_field(self, ptr, name):
-        return native_bt.field_class_variant_add_field(self._ptr, ptr, name)
+    def _set_selector_field_class(self, selector_fc):
+        utils._check_type(selector_fc, bt2.field_class._EnumerationFieldClass)
+        ret = native_bt.field_class_variant_set_selector_field_class(self._ptr, selector_fc._ptr)
+        utils._handle_ret(ret, "cannot set variant selector field type")
 
-    def _at(self, index):
-        if index < 0 or index >= len(self):
-            raise IndexError
+    _selector_field_class = property(fset=_set_selector_field_class)
 
-        ret, name, field_class_ptr = native_bt.field_class_variant_get_field_by_index(self._ptr, index)
-        assert(ret == 0)
-        return _create_from_ptr(field_class_ptr)
 
+class _ArrayFieldClass(_FieldClass):
+    @property
+    def element_field_class(self):
+        elem_fc_ptr = native_bt.field_class_array_borrow_element_field_class_const(self._ptr)
+        return _create_field_class_from_ptr_and_get_ref(elem_fc_ptr)
 
-class ArrayFieldClass(_FieldClass):
-    _NAME = 'Array'
-
-    def __init__(self, element_field_class, length):
-        utils._check_type(element_field_class, _FieldClass)
-        utils._check_uint64(length)
-        ptr = native_bt.field_class_array_create(element_field_class._ptr, length)
-        self._check_create_status(ptr)
-        super().__init__(ptr)
 
+class _StaticArrayFieldClass(_ArrayFieldClass):
     @property
     def length(self):
-        length = native_bt.field_class_array_get_length(self._ptr)
-        assert(length >= 0)
-        return length
-
-    @property
-    def element_field_class(self):
-        ptr = native_bt.field_class_array_get_element_type(self._ptr)
-        assert(ptr)
-        return _create_from_ptr(ptr)
+        return native_bt.field_class_static_array_get_length(self._ptr)
 
 
-class SequenceFieldClass(_FieldClass):
-    _NAME = 'Sequence'
+class _DynamicArrayFieldClass(_ArrayFieldClass):
+    @property
+    def length_field_path(self):
+        ptr = native_bt.field_class_dynamic_array_borrow_length_field_path_const(self._ptr)
+        if ptr is None:
+            return
 
-    def __init__(self, element_field_class, length_name):
-        utils._check_type(element_field_class, _FieldClass)
-        utils._check_str(length_name)
-        ptr = native_bt.field_class_sequence_create(element_field_class._ptr,
-                                                   length_name)
-        self._check_create_status(ptr)
-        super().__init__(ptr)
+        return bt2.field_path._FieldPath._create_from_ptr_and_get_ref(ptr)
 
-    @property
-    def length_name(self):
-        length_name = native_bt.field_class_sequence_get_length_field_name(self._ptr)
-        assert(length_name is not None)
-        return length_name
+    def _set_length_field_class(self, length_fc):
+        utils._check_type(length_fc, _UnsignedIntegerFieldClass)
+        ret = native_bt.field_class_dynamic_array_set_length_field_class(self._ptr, length_fc._ptr)
+        utils._handle_ret(ret, "cannot set dynamic array length field type")
 
-    @property
-    def element_field_class(self):
-        ptr = native_bt.field_class_sequence_get_element_type(self._ptr)
-        assert(ptr)
-        return _create_from_ptr(ptr)
+    _length_field_class = property(fset=_set_length_field_class)
 
 
 _FIELD_CLASS_TYPE_TO_OBJ = {
+    native_bt.FIELD_CLASS_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerFieldClass,
+    native_bt.FIELD_CLASS_TYPE_SIGNED_INTEGER: _SignedIntegerFieldClass,
+    native_bt.FIELD_CLASS_TYPE_REAL: _RealFieldClass,
+    native_bt.FIELD_CLASS_TYPE_UNSIGNED_ENUMERATION: _UnsignedEnumerationFieldClass,
+    native_bt.FIELD_CLASS_TYPE_SIGNED_ENUMERATION: _SignedEnumerationFieldClass,
+    native_bt.FIELD_CLASS_TYPE_STRING: _StringFieldClass,
     native_bt.FIELD_CLASS_TYPE_STRUCTURE: _StructureFieldClass,
+    native_bt.FIELD_CLASS_TYPE_STATIC_ARRAY: _StaticArrayFieldClass,
+    native_bt.FIELD_CLASS_TYPE_DYNAMIC_ARRAY: _DynamicArrayFieldClass,
+    native_bt.FIELD_CLASS_TYPE_VARIANT: _VariantFieldClass,
 }
diff --git a/bindings/python/bt2/bt2/field_path.py b/bindings/python/bt2/bt2/field_path.py
new file mode 100644 (file)
index 0000000..2191171
--- /dev/null
@@ -0,0 +1,82 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2018 Francis Deslauriers <francis.deslauriers@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.
+
+import collections
+from bt2 import native_bt, object
+
+
+class Scope:
+    PACKET_CONTEXT = native_bt.SCOPE_PACKET_CONTEXT
+    EVENT_COMMON_CONTEXT = native_bt.SCOPE_EVENT_COMMON_CONTEXT
+    EVENT_SPECIFIC_CONTEXT = native_bt.SCOPE_EVENT_SPECIFIC_CONTEXT
+    EVENT_PAYLOAD = native_bt.SCOPE_EVENT_PAYLOAD
+
+
+class _FieldPathItem:
+    pass
+
+
+class _IndexFieldPathItem(_FieldPathItem):
+    def __init__(self, index):
+        self._index = index
+
+    @property
+    def index(self):
+        return self._index
+
+
+class _CurrentArrayElementFieldPathItem(_FieldPathItem):
+    pass
+
+
+class _FieldPath(object._SharedObject, collections.abc.Iterable):
+    _get_ref = staticmethod(native_bt.field_path_get_ref)
+    _put_ref = staticmethod(native_bt.field_path_put_ref)
+
+    @property
+    def root_scope(self):
+        scope = native_bt.field_path_get_root_scope(self._ptr)
+        return _SCOPE_TO_OBJ[scope]
+
+    def __len__(self):
+        return native_bt.field_path_get_item_count(self._ptr)
+
+    def __iter__(self):
+        for idx in range(len(self)):
+            item_ptr = native_bt.field_path_borrow_item_by_index_const(self._ptr, idx)
+            assert item_ptr is not None
+            item_type = native_bt.field_path_item_get_type(item_ptr)
+            if item_type == native_bt.FIELD_PATH_ITEM_TYPE_INDEX:
+                idx = native_bt.field_path_item_index_get_index(item_ptr)
+                yield _IndexFieldPathItem(idx)
+            elif item_type == native_bt.FIELD_PATH_ITEM_TYPE_CURRENT_ARRAY_ELEMENT:
+                yield  _CurrentArrayElementFieldPathItem()
+            else:
+                assert False
+
+
+_SCOPE_TO_OBJ = {
+    native_bt.SCOPE_PACKET_CONTEXT: Scope.PACKET_CONTEXT,
+    native_bt.SCOPE_EVENT_COMMON_CONTEXT: Scope.EVENT_COMMON_CONTEXT,
+    native_bt.SCOPE_EVENT_SPECIFIC_CONTEXT: Scope.EVENT_SPECIFIC_CONTEXT,
+    native_bt.SCOPE_EVENT_PAYLOAD: Scope.EVENT_PAYLOAD
+}
index f8ef2646566a88e35c11c1d75c0e89022832a60d..9ae57ade86e61c59b2487ce2bc712cc22ac2dc61 100644 (file)
@@ -224,37 +224,47 @@ class TraceClass(object._SharedObject, collections.abc.Mapping):
             raise bt2.CreationError(
                 'cannot create {} field class'.format(type_name))
 
-    def _create_integer_field_class(self, create_func, py_cls, type_name, range, display_base):
+    def _create_integer_field_class(self, create_func, py_cls, type_name, field_value_range, preferred_display_base):
         field_class_ptr = create_func(self._ptr)
         self._check_create_status(field_class_ptr, type_name)
 
         field_class = py_cls._create_from_ptr(field_class_ptr)
 
-        if range is not None:
-            field_class._range = range
+        if field_value_range is not None:
+            field_class._field_value_range = field_value_range
 
-        if display_base is not None:
-            field_class._display_base = display_base
+        if preferred_display_base is not None:
+            field_class._preferred_display_base = preferred_display_base
 
         return field_class
 
-    def create_signed_integer_field_class(self, range=None, display_base=None):
+    def create_signed_integer_field_class(self, field_value_range=None, preferred_display_base=None):
         return self._create_integer_field_class(native_bt.field_class_signed_integer_create,
-                                                bt2.field_class.SignedIntegerFieldClass,
-                                                'signed integer', range, display_base)
+                                                bt2.field_class._SignedIntegerFieldClass,
+                                                'signed integer', field_value_range, preferred_display_base)
 
-    def create_unsigned_integer_field_class(self, range=None, display_base=None):
+    def create_unsigned_integer_field_class(self, field_value_range=None, preferred_display_base=None):
         return self._create_integer_field_class(native_bt.field_class_unsigned_integer_create,
-                                                bt2.field_class.UnsignedIntegerFieldClass,
-                                                'unsigned integer', range, display_base)
+                                                bt2.field_class._UnsignedIntegerFieldClass,
+                                                'unsigned integer', field_value_range, preferred_display_base)
+
+    def create_signed_enumeration_field_class(self, field_value_range=None, preferred_display_base=None):
+        return self._create_integer_field_class(native_bt.field_class_signed_enumeration_create,
+                                                bt2.field_class._SignedEnumerationFieldClass,
+                                                'signed enumeration', field_value_range, preferred_display_base)
+
+    def create_unsigned_enumeration_field_class(self, field_value_range=None, preferred_display_base=None):
+        return self._create_integer_field_class(native_bt.field_class_unsigned_enumeration_create,
+                                                bt2.field_class._UnsignedEnumerationFieldClass,
+                                                'unsigned enumeration', field_value_range, preferred_display_base)
 
     def create_real_field_class(self, is_single_precision=False):
         field_class_ptr = native_bt.field_class_real_create(self._ptr)
         self._check_create_status(field_class_ptr, 'real')
 
-        field_class = bt2.field_class.RealFieldClass._create_from_ptr(field_class_ptr)
+        field_class = bt2.field_class._RealFieldClass._create_from_ptr(field_class_ptr)
 
-        field_class._single_precision = is_single_precision
+        field_class._is_single_precision = is_single_precision
 
         return field_class
 
@@ -268,7 +278,36 @@ class TraceClass(object._SharedObject, collections.abc.Mapping):
         field_class_ptr = native_bt.field_class_string_create(self._ptr)
         self._check_create_status(field_class_ptr, 'string')
 
-        return bt2.field_class.StringFieldClass._create_from_ptr(field_class_ptr)
+        return bt2.field_class._StringFieldClass._create_from_ptr(field_class_ptr)
+
+    def create_static_array_field_class(self, elem_fc, length):
+        utils._check_type(elem_fc, bt2.field_class._FieldClass)
+        utils._check_uint64(length)
+        ptr = native_bt.field_class_static_array_create(self._ptr, elem_fc._ptr, length)
+        self._check_create_status(ptr, 'static array')
+
+        return bt2.field_class._StaticArrayFieldClass._create_from_ptr_and_get_ref(ptr)
+
+    def create_dynamic_array_field_class(self, elem_fc, length_fc=None):
+        utils._check_type(elem_fc, bt2.field_class._FieldClass)
+        ptr = native_bt.field_class_dynamic_array_create(self._ptr, elem_fc._ptr)
+        self._check_create_status(ptr, 'dynamic array')
+        obj = bt2.field_class._DynamicArrayFieldClass._create_from_ptr(ptr)
+
+        if length_fc is not None:
+            obj._length_field_class = length_fc
+
+        return obj
+
+    def create_variant_field_class(self, selector_fc=None):
+        ptr = native_bt.field_class_variant_create(self._ptr)
+        self._check_create_status(ptr, 'variant')
+        obj = bt2.field_class._VariantFieldClass._create_from_ptr(ptr)
+
+        if selector_fc is not None:
+            obj._selector_field_class = selector_fc
+
+        return obj
 
     # Add a listener to be called when the trace class is destroyed.
 
index 4d2fe60a25d65e2c2fd5ff7fcbe3d67bed4474c4..fdde035315658919711505a4a925dbf0d545843c 100644 (file)
@@ -8,11 +8,11 @@ class EventClassTestCase(unittest.TestCase):
         self._tc = get_default_trace_class()
 
         self._context_fc = self._tc.create_structure_field_class()
-        self._context_fc.append_field('allo', self._tc.create_string_field_class())
-        self._context_fc.append_field('zola', self._tc.create_signed_integer_field_class(18))
+        self._context_fc.append_member('allo', self._tc.create_string_field_class())
+        self._context_fc.append_member('zola', self._tc.create_signed_integer_field_class(18))
 
         self._payload_fc = self._tc.create_structure_field_class()
-        self._payload_fc.append_field('zoom', self._tc.create_string_field_class())
+        self._payload_fc.append_member('zoom', self._tc.create_string_field_class())
 
         self._stream_class = self._tc.create_stream_class(assigns_automatic_event_class_id=True)
 
index ca524bb0ac53131d25b8cdc17c6d36238441def5..4ca01d866b98fa4a2c797b2f3f19563ec9e23a9f 100644 (file)
 import bt2.field
 import unittest
-import copy
 import bt2
+from utils import get_default_trace_class
 
 
-class _TestCopySimple:
-    def _test_copy(self, cpy):
-        self.assertIsNot(cpy, self._fc)
-        self.assertNotEqual(cpy.addr, self._fc.addr)
-        self.assertEqual(cpy, self._fc)
+class _TestIntegerFieldClassProps:
+    def test_create_default(self):
+        fc = self._create_func()
+        self.assertEqual(fc.field_value_range, 64)
+        self.assertEqual(fc.preferred_display_base, bt2.IntegerDisplayBase.DECIMAL)
 
-    def test_copy(self):
-        cpy = copy.copy(self._fc)
-        self._test_copy(cpy)
+    def test_create_range(self):
+        fc = self._create_func(field_value_range=35)
+        self.assertEqual(fc.field_value_range, 35)
 
-    def test_deepcopy(self):
-        cpy = copy.deepcopy(self._fc)
-        self._test_copy(cpy)
+        fc = self._create_func(36)
+        self.assertEqual(fc.field_value_range, 36)
 
+    def test_create_invalid_range(self):
+        with self.assertRaises(TypeError):
+            self._create_func('yes')
 
-class _TestAlignmentProp:
-    def test_assign_alignment(self):
-        self._fc.alignment = 32
-        self.assertEqual(self._fc.alignment, 32)
+        with self.assertRaises(TypeError):
+            self._create_func(field_value_range='yes')
 
-    def test_assign_invalid_alignment(self):
         with self.assertRaises(ValueError):
-            self._fc.alignment = 23
+            self._create_func(field_value_range=-2)
 
+        with self.assertRaises(ValueError):
+            self._create_func(field_value_range=0)
 
-class _TestByteOrderProp:
-    def test_assign_byte_order(self):
-        self._fc.byte_order = bt2.ByteOrder.LITTLE_ENDIAN
-        self.assertEqual(self._fc.byte_order, bt2.ByteOrder.LITTLE_ENDIAN)
+    def test_create_base(self):
+        fc = self._create_func(preferred_display_base=bt2.IntegerDisplayBase.HEXADECIMAL)
+        self.assertEqual(fc.preferred_display_base, bt2.IntegerDisplayBase.HEXADECIMAL)
 
-    def test_assign_invalid_byte_order(self):
+    def test_create_invalid_base_type(self):
         with self.assertRaises(TypeError):
-            self._fc.byte_order = 'hey'
+            self._create_func(preferred_display_base='yes')
 
+    def test_create_invalid_base_value(self):
+        with self.assertRaises(ValueError):
+            self._create_func(preferred_display_base=444)
 
-class _TestInvalidEq:
-    def test_eq_invalid(self):
-        self.assertFalse(self._fc == 23)
+    def test_create_full(self):
+        fc = self._create_func(24, preferred_display_base=bt2.IntegerDisplayBase.OCTAL)
+        self.assertEqual(fc.field_value_range, 24)
+        self.assertEqual(fc.preferred_display_base, bt2.IntegerDisplayBase.OCTAL)
 
 
-class _TestIntegerFieldClassProps:
-    def test_size_prop(self):
-        self.assertEqual(self._fc.size, 35)
+class IntegerFieldClassTestCase(_TestIntegerFieldClassProps, unittest.TestCase):
+    def setUp(self):
+        self._tc = get_default_trace_class()
+        self._create_func = self._tc.create_signed_integer_field_class
 
-    def test_assign_signed(self):
-        self._fc.is_signed = True
-        self.assertTrue(self._fc.is_signed)
 
-    def test_assign_invalid_signed(self):
-        with self.assertRaises(TypeError):
-            self._fc.is_signed = 23
+class RealFieldClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self._tc = get_default_trace_class()
 
-    def test_assign_base(self):
-        self._fc.base = bt2.Base.HEXADECIMAL
-        self.assertEqual(self._fc.base, bt2.Base.HEXADECIMAL)
+    def test_create_default(self):
+        fc = self._tc.create_real_field_class()
+        self.assertFalse(fc.is_single_precision)
 
-    def test_assign_invalid_base(self):
+    def test_create_is_single_precision(self):
+        fc = self._tc.create_real_field_class(is_single_precision=True)
+        self.assertTrue(fc.is_single_precision)
+
+    def test_create_invalid_is_single_precision(self):
         with self.assertRaises(TypeError):
-            self._fc.base = 'hey'
+            self._tc.create_real_field_class(is_single_precision='hohoho')
 
-    def test_assign_encoding(self):
-        self._fc.encoding = bt2.Encoding.UTF8
-        self.assertEqual(self._fc.encoding, bt2.Encoding.UTF8)
 
-    def test_assign_invalid_encoding(self):
-        with self.assertRaises(TypeError):
-            self._fc.encoding = 'hey'
+# Converts an _EnumerationFieldClassMapping to a list of ranges:
+#
+#    [(lower0, upper0), (lower1, upper1), ...]
+
+def enum_mapping_to_list(mapping):
+    return sorted([(x.lower, x.upper) for x in mapping])
 
-    def test_assign_mapped_clock_class(self):
-        cc = bt2.ClockClass('name', 1000)
-        self._fc.mapped_clock_class = cc
-        self.assertEqual(self._fc.mapped_clock_class, cc)
 
-    def test_assign_invalid_mapped_clock_class(self):
+class EnumerationFieldClassTestCase(_TestIntegerFieldClassProps):
+    def setUp(self):
+        self._tc = get_default_trace_class()
+
+    def test_create_from_invalid_type(self):
         with self.assertRaises(TypeError):
-            self._fc.mapped_clock_class = object()
+            self._create_func('coucou')
 
+    def test_add_mapping_simple(self):
+        self._fc.map_range('hello', 24)
+        mapping = self._fc['hello']
+        self.assertEqual(mapping.label, 'hello')
 
-@unittest.skip("this is broken")
-class IntegerFieldClassTestCase(_TestIntegerFieldClassProps, _TestCopySimple,
-                               _TestAlignmentProp, _TestByteOrderProp,
-                               _TestInvalidEq, unittest.TestCase):
-    def setUp(self):
-        self._fc = bt2.IntegerFieldClass(35)
+        ranges = enum_mapping_to_list(mapping)
+        self.assertEqual(ranges, [(24, 24)])
 
-    def tearDown(self):
-        del self._fc
+    def test_add_mapping_simple_kwargs(self):
+        self._fc.map_range(label='hello', lower=17, upper=23)
+        mapping = self._fc['hello']
+        self.assertEqual(mapping.label, 'hello')
 
-    def test_create_default(self):
-        self.assertEqual(self._fc.size, 35)
-        self.assertIsNone(self._fc.mapped_clock_class)
+        ranges = enum_mapping_to_list(mapping)
+        self.assertEqual(ranges, [(17, 23)])
 
-    def test_create_invalid_size(self):
+    def test_add_mapping_range(self):
+        self._fc.map_range('hello', 21, 199)
+        mapping = self._fc['hello']
+        self.assertEqual(mapping.label, 'hello')
+
+        ranges = enum_mapping_to_list(mapping)
+        self.assertEqual(ranges, [(21, 199)])
+
+    def test_add_mapping_invalid_name(self):
         with self.assertRaises(TypeError):
-            fc = bt2.IntegerFieldClass('yes')
+            self._fc.map_range(17, 21, 199)
 
-    def test_create_neg_size(self):
-        with self.assertRaises(ValueError):
-            fc = bt2.IntegerFieldClass(-2)
+    def test_iadd(self):
+        enum_fc = self._tc.create_signed_enumeration_field_class(field_value_range=16)
+        enum_fc.map_range('c', 4, 5)
+        enum_fc.map_range('d', 6, 18)
+        enum_fc.map_range('e', 20, 27)
+        self._fc.map_range('a', 0, 2)
+        self._fc.map_range('b', 3)
+        self._fc += enum_fc
 
-    def test_create_neg_zero(self):
-        with self.assertRaises(ValueError):
-            fc = bt2.IntegerFieldClass(0)
+        self.assertEqual(self._fc['a'].label, 'a')
+        self.assertEqual(enum_mapping_to_list(self._fc['a']), [(0, 2)])
 
-    def test_create_full(self):
-        cc = bt2.ClockClass('name', 1000)
-        fc = bt2.IntegerFieldClass(24, alignment=16,
-                                  byte_order=bt2.ByteOrder.BIG_ENDIAN,
-                                  is_signed=True, base=bt2.Base.OCTAL,
-                                  encoding=bt2.Encoding.NONE,
-                                  mapped_clock_class=cc)
-        self.assertEqual(fc.size, 24)
-        self.assertEqual(fc.alignment, 16)
-        self.assertEqual(fc.byte_order, bt2.ByteOrder.BIG_ENDIAN)
-        self.assertTrue(fc.is_signed)
-        self.assertEqual(fc.base, bt2.Base.OCTAL)
-        self.assertEqual(fc.encoding, bt2.Encoding.NONE)
-        self.assertEqual(fc.mapped_clock_class, cc)
-
-    def test_create_field(self):
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._IntegerField)
-
-    def test_create_field_init(self):
-        field = self._fc(23)
-        self.assertEqual(field, 23)
-
-
-@unittest.skip("this is broken")
-class FloatingPointNumberFieldClassTestCase(_TestCopySimple, _TestAlignmentProp,
-                                           _TestByteOrderProp, _TestInvalidEq,
-                                           unittest.TestCase):
-    def setUp(self):
-        self._fc = bt2.FloatingPointNumberFieldClass()
+        self.assertEqual(self._fc['b'].label, 'b')
+        self.assertEqual(enum_mapping_to_list(self._fc['b']), [(3, 3)])
 
-    def tearDown(self):
-        del self._fc
+        self.assertEqual(self._fc['c'].label, 'c')
+        self.assertEqual(enum_mapping_to_list(self._fc['c']), [(4, 5)])
 
-    def test_create_default(self):
-        pass
+        self.assertEqual(self._fc['d'].label, 'd')
+        self.assertEqual(enum_mapping_to_list(self._fc['d']), [(6, 18)])
 
-    def test_create_full(self):
-        fc = bt2.FloatingPointNumberFieldClass(alignment=16,
-                                              byte_order=bt2.ByteOrder.BIG_ENDIAN,
-                                              exponent_size=11,
-                                              mantissa_size=53)
-        self.assertEqual(fc.alignment, 16)
-        self.assertEqual(fc.byte_order, bt2.ByteOrder.BIG_ENDIAN)
-        self.assertEqual(fc.exponent_size, 11)
-        self.assertEqual(fc.mantissa_size, 53)
-
-    def test_assign_exponent_size(self):
-        self._fc.exponent_size = 8
-        self.assertEqual(self._fc.exponent_size, 8)
-
-    def test_assign_invalid_exponent_size(self):
-        with self.assertRaises(TypeError):
-            self._fc.exponent_size = 'yes'
+        self.assertEqual(self._fc['e'].label, 'e')
+        self.assertEqual(enum_mapping_to_list(self._fc['e']), [(20, 27)])
 
-    def test_assign_mantissa_size(self):
-        self._fc.mantissa_size = 24
-        self.assertEqual(self._fc.mantissa_size, 24)
+    def test_bool_op(self):
+        self.assertFalse(self._fc)
+        self._fc.map_range('a', 0)
+        self.assertTrue(self._fc)
 
-    def test_assign_invalid_mantissa_size(self):
-        with self.assertRaises(TypeError):
-            self._fc.mantissa_size = 'no'
+    def test_len(self):
+        self._fc.map_range('a', 0)
+        self._fc.map_range('b', 1)
+        self._fc.map_range('c', 2)
+        self.assertEqual(len(self._fc), 3)
 
-    def test_create_field(self):
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._FloatingPointNumberField)
+    def test_getitem(self):
+        self._fc.map_range('a', 0)
+        self._fc.map_range('b', 1, 3)
+        self._fc.map_range('a', 5)
+        self._fc.map_range('a', 17, 123)
+        self._fc.map_range('C', 5)
+        mapping = self._fc['a']
 
-    def test_create_field_init(self):
-        field = self._fc(17.5)
-        self.assertEqual(field, 17.5)
+        self.assertEqual(mapping.label, 'a')
+        ranges = enum_mapping_to_list(mapping)
+        self.assertEqual(ranges, [(0, 0), (5, 5), (17, 123)])
 
+        with self.assertRaises(KeyError):
+            self._fc['doesnotexist']
 
-@unittest.skip("this is broken")
-class EnumerationFieldClassTestCase(_TestIntegerFieldClassProps, _TestInvalidEq,
-                                   _TestCopySimple, _TestAlignmentProp,
-                                   _TestByteOrderProp, unittest.TestCase):
-    def setUp(self):
-        self._fc = bt2.EnumerationFieldClass(size=35)
+    def test_contains(self):
+        self._fc.map_range('a', 0)
+        self._fc.map_range('a', 2, 23)
+        self._fc.map_range('b', 2)
+        self._fc.map_range('c', 5)
 
-    def tearDown(self):
-        del self._fc
+        a_mapping = self._fc['a']
+        b_mapping = self._fc['b']
+        first_range = next(iter(a_mapping))
 
-    def test_create_from_int_fc(self):
-        int_fc = bt2.IntegerFieldClass(23)
-        self._fc = bt2.EnumerationFieldClass(int_fc)
+        self.assertIn(first_range, a_mapping)
+        self.assertNotIn(first_range, b_mapping)
 
-    def test_create_from_invalid_type(self):
-        with self.assertRaises(TypeError):
-            self._fc = bt2.EnumerationFieldClass('coucou')
+    def test_iter(self):
+        self._fc.map_range('a', 1, 5)
+        self._fc.map_range('b', 10, 17)
+        self._fc.map_range('c', 20, 1504)
 
-    def test_create_from_invalid_fc(self):
-        with self.assertRaises(TypeError):
-            fc = bt2.FloatingPointNumberFieldClass()
-            self._fc = bt2.EnumerationFieldClass(fc)
+        self._fc.map_range('d', 22510, 99999)
 
-    def test_create_full(self):
-        fc = bt2.EnumerationFieldClass(size=24, alignment=16,
-                                      byte_order=bt2.ByteOrder.BIG_ENDIAN,
-                                      is_signed=True, base=bt2.Base.OCTAL,
-                                      encoding=bt2.Encoding.NONE,
-                                      mapped_clock_class=None)
-        self.assertEqual(fc.size, 24)
-        self.assertEqual(fc.alignment, 16)
-        self.assertEqual(fc.byte_order, bt2.ByteOrder.BIG_ENDIAN)
-        self.assertTrue(fc.is_signed)
-        self.assertEqual(fc.base, bt2.Base.OCTAL)
-        self.assertEqual(fc.encoding, bt2.Encoding.NONE)
-        #self.assertIsNone(fc.mapped_clock_class)
-
-    def test_integer_field_class_prop(self):
-        int_fc = bt2.IntegerFieldClass(23)
-        enum_fc = bt2.EnumerationFieldClass(int_fc)
-        self.assertEqual(enum_fc.integer_field_class.addr, int_fc.addr)
+        # This exercises iteration.
+        labels = sorted(self._fc)
 
-    def test_add_mapping_simple(self):
-        self._fc.add_mapping('hello', 24)
-        mapping = self._fc[0]
-        self.assertEqual(mapping.name, 'hello')
-        self.assertEqual(mapping.lower, 24)
-        self.assertEqual(mapping.upper, 24)
+        self.assertEqual(labels, ['a', 'b', 'c', 'd'])
 
-    def test_add_mapping_simple_kwargs(self):
-        self._fc.add_mapping(name='hello', lower=17, upper=23)
-        mapping = self._fc[0]
-        self.assertEqual(mapping.name, 'hello')
-        self.assertEqual(mapping.lower, 17)
-        self.assertEqual(mapping.upper, 23)
+    def test_find_by_value(self):
+        self._fc.map_range('a', 0)
+        self._fc.map_range('b', 1, 3)
+        self._fc.map_range('c', 5, 19)
+        self._fc.map_range('d', 8, 15)
+        self._fc.map_range('e', 10, 21)
+        self._fc.map_range('f', 0)
+        self._fc.map_range('g', 14)
 
-    def test_add_mapping_range(self):
-        self._fc.add_mapping('hello', 21, 199)
-        mapping = self._fc[0]
-        self.assertEqual(mapping.name, 'hello')
-        self.assertEqual(mapping.lower, 21)
-        self.assertEqual(mapping.upper, 199)
+        labels = self._fc.labels_by_value(14)
 
-    def test_add_mapping_invalid_name(self):
-        with self.assertRaises(TypeError):
-            self._fc.add_mapping(17, 21, 199)
+        expected_labels = ['c', 'd', 'e', 'g']
+
+        self.assertTrue(all(label in labels for label in expected_labels))
+
+
+class UnsignedEnumerationFieldClassTestCase(EnumerationFieldClassTestCase, unittest.TestCase):
+    def setUp(self):
+        super().setUp()
+        self._create_func = self._tc.create_unsigned_enumeration_field_class
+        self._fc = self._tc.create_unsigned_enumeration_field_class()
 
     def test_add_mapping_invalid_signedness_lower(self):
         with self.assertRaises(ValueError):
-            self._fc.add_mapping('hello', -21, 199)
+            self._fc.map_range('hello', -21, 199)
 
     def test_add_mapping_invalid_signedness_upper(self):
         with self.assertRaises(ValueError):
-            self._fc.add_mapping('hello', 21, -199)
+            self._fc.map_range('hello', 21, -199)
 
-    def test_add_mapping_simple_signed(self):
-        self._fc.is_signed = True
-        self._fc.add_mapping('hello', -24)
-        mapping = self._fc[0]
-        self.assertEqual(mapping.name, 'hello')
-        self.assertEqual(mapping.lower, -24)
-        self.assertEqual(mapping.upper, -24)
 
-    def test_add_mapping_range_signed(self):
-        self._fc.is_signed = True
-        self._fc.add_mapping('hello', -21, 199)
-        mapping = self._fc[0]
-        self.assertEqual(mapping.name, 'hello')
-        self.assertEqual(mapping.lower, -21)
-        self.assertEqual(mapping.upper, 199)
+class SignedEnumerationFieldClassTestCase(EnumerationFieldClassTestCase, unittest.TestCase):
+    def setUp(self):
+        super().setUp()
+        self._create_func = self._tc.create_signed_enumeration_field_class
+        self._fc = self._tc.create_signed_enumeration_field_class()
 
-    def test_iadd(self):
-        enum_fc = bt2.EnumerationFieldClass(size=16)
-        enum_fc.add_mapping('c', 4, 5)
-        enum_fc.add_mapping('d', 6, 18)
-        enum_fc.add_mapping('e', 20, 27)
-        self._fc.add_mapping('a', 0, 2)
-        self._fc.add_mapping('b', 3)
-        self._fc += enum_fc
-        self.assertEqual(self._fc[0].name, 'a')
-        self.assertEqual(self._fc[0].lower, 0)
-        self.assertEqual(self._fc[0].upper, 2)
-        self.assertEqual(self._fc[1].name, 'b')
-        self.assertEqual(self._fc[1].lower, 3)
-        self.assertEqual(self._fc[1].upper, 3)
-        self.assertEqual(self._fc[2].name, 'c')
-        self.assertEqual(self._fc[2].lower, 4)
-        self.assertEqual(self._fc[2].upper, 5)
-        self.assertEqual(self._fc[3].name, 'd')
-        self.assertEqual(self._fc[3].lower, 6)
-        self.assertEqual(self._fc[3].upper, 18)
-        self.assertEqual(self._fc[4].name, 'e')
-        self.assertEqual(self._fc[4].lower, 20)
-        self.assertEqual(self._fc[4].upper, 27)
+    def test_add_mapping_simple_signed(self):
+        self._fc.map_range('hello', -24)
+        mapping = self._fc['hello']
+        self.assertEqual(mapping.label, 'hello')
 
-    def test_bool_op(self):
-        self.assertFalse(self._fc)
-        self._fc.add_mapping('a', 0)
-        self.assertTrue(self._fc)
+        ranges = enum_mapping_to_list(mapping)
+        self.assertEqual(ranges, [(-24, -24)])
 
-    def test_len(self):
-        self._fc.add_mapping('a', 0)
-        self._fc.add_mapping('b', 1)
-        self._fc.add_mapping('c', 2)
-        self.assertEqual(len(self._fc), 3)
+    def test_add_mapping_range_signed(self):
+        self._fc.map_range('hello', -21, 199)
+        mapping = self._fc['hello']
+        self.assertEqual(mapping.label, 'hello')
+        ranges = enum_mapping_to_list(mapping)
+        self.assertEqual(ranges, [(-21, 199)])
 
-    def test_getitem(self):
-        self._fc.add_mapping('a', 0)
-        self._fc.add_mapping('b', 1, 3)
-        self._fc.add_mapping('c', 5)
-        mapping = self._fc[1]
-        self.assertEqual(mapping.name, 'b')
-        self.assertEqual(mapping.lower, 1)
-        self.assertEqual(mapping.upper, 3)
 
-    def test_iter(self):
-        mappings = (
-            ('a', 1, 5),
-            ('b', 10, 17),
-            ('c', 20, 1504),
-            ('d', 22510, 99999),
-        )
-
-        for mapping in mappings:
-            self._fc.add_mapping(*mapping)
-
-        for fc_mapping, mapping in zip(self._fc, mappings):
-            self.assertEqual(fc_mapping.name, mapping[0])
-            self.assertEqual(fc_mapping.lower, mapping[1])
-            self.assertEqual(fc_mapping.upper, mapping[2])
-
-    def test_mapping_eq(self):
-        enum1 = bt2.EnumerationFieldClass(size=32)
-        enum2 = bt2.EnumerationFieldClass(size=16)
-        enum1.add_mapping('b', 1, 3)
-        enum2.add_mapping('b', 1, 3)
-        self.assertEqual(enum1[0], enum2[0])
-
-    def test_mapping_eq_invalid(self):
-        enum1 = bt2.EnumerationFieldClass(size=32)
-        enum1.add_mapping('b', 1, 3)
-        self.assertNotEqual(enum1[0], 23)
-
-    def _test_find_by_name(self, fc):
-        fc.add_mapping('a', 0)
-        fc.add_mapping('b', 1, 3)
-        fc.add_mapping('a', 5)
-        fc.add_mapping('a', 17, 144)
-        fc.add_mapping('C', 5)
-        mapping_iter = fc.mappings_by_name('a')
-        mappings = list(mapping_iter)
-        a0 = False
-        a5 = False
-        a17_144 = False
-        i = 0
-
-        for mapping in mappings:
-            i += 1
-            self.assertEqual(mapping.name, 'a')
-
-            if mapping.lower == 0 and mapping.upper == 0:
-                a0 = True
-            elif mapping.lower == 5 and mapping.upper == 5:
-                a5 = True
-            elif mapping.lower == 17 and mapping.upper == 144:
-                a17_144 = True
-
-        self.assertEqual(i, 3)
-        self.assertTrue(a0)
-        self.assertTrue(a5)
-        self.assertTrue(a17_144)
-
-    def test_find_by_name_signed(self):
-        self._test_find_by_name(bt2.EnumerationFieldClass(size=8, is_signed=True))
-
-    def test_find_by_name_unsigned(self):
-        self._test_find_by_name(bt2.EnumerationFieldClass(size=8))
-
-    def _test_find_by_value(self, fc):
-        fc.add_mapping('a', 0)
-        fc.add_mapping('b', 1, 3)
-        fc.add_mapping('c', 5, 19)
-        fc.add_mapping('d', 8, 15)
-        fc.add_mapping('e', 10, 21)
-        fc.add_mapping('f', 0)
-        fc.add_mapping('g', 14)
-        mapping_iter = fc.mappings_by_value(14)
-        mappings = list(mapping_iter)
-        c = False
-        d = False
-        e = False
-        g = False
-        i = 0
-
-        for mapping in mappings:
-            i += 1
-
-            if mapping.name == 'c':
-                c = True
-            elif mapping.name == 'd':
-                d = True
-            elif mapping.name == 'e':
-                e = True
-            elif mapping.name == 'g':
-                g = True
-
-        self.assertEqual(i, 4)
-        self.assertTrue(c)
-        self.assertTrue(d)
-        self.assertTrue(e)
-        self.assertTrue(g)
-
-    def test_find_by_value_signed(self):
-        self._test_find_by_value(bt2.EnumerationFieldClass(size=8, is_signed=True))
-
-    def test_find_by_value_unsigned(self):
-        self._test_find_by_value(bt2.EnumerationFieldClass(size=8))
-
-    def test_create_field(self):
-        self._fc.add_mapping('c', 4, 5)
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._EnumerationField)
-
-    def test_create_field_init(self):
-        self._fc.add_mapping('c', 4, 5)
-        field = self._fc(4)
-        self.assertEqual(field, 4)
-
-
-@unittest.skip("this is broken")
-class StringFieldClassTestCase(_TestCopySimple, _TestInvalidEq,
-                              unittest.TestCase):
+class StringFieldClassTestCase(unittest.TestCase):
     def setUp(self):
-        self._fc = bt2.StringFieldClass()
-
-    def tearDown(self):
-        del self._fc
+        tc = get_default_trace_class()
+        self._fc = tc.create_string_field_class()
 
     def test_create_default(self):
-        pass
-
-    def test_create_full(self):
-        fc = bt2.StringFieldClass(encoding=bt2.Encoding.UTF8)
-        self.assertEqual(fc.encoding, bt2.Encoding.UTF8)
-
-    def test_assign_encoding(self):
-        self._fc.encoding = bt2.Encoding.UTF8
-        self.assertEqual(self._fc.encoding, bt2.Encoding.UTF8)
-
-    def test_assign_invalid_encoding(self):
-        with self.assertRaises(TypeError):
-            self._fc.encoding = 'yes'
-
-    def test_create_field(self):
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._StringField)
+        self.assertIsNotNone(self._fc)
 
-    def test_create_field_init(self):
-        field = self._fc('hola')
-        self.assertEqual(field, 'hola')
 
-
-class _TestFieldContainer(_TestInvalidEq, _TestCopySimple):
-    def test_append_field(self):
-        int_field_class = bt2.IntegerFieldClass(32)
-        self._fc.append_field('int32', int_field_class)
+class _TestFieldContainer():
+    def test_append_element(self):
+        int_field_class = self._tc.create_signed_integer_field_class(32)
+        self._append_element_method(self._fc, 'int32', int_field_class)
         field_class = self._fc['int32']
-        self.assertEqual(field_class, int_field_class)
+        self.assertEqual(field_class.addr, int_field_class.addr)
 
-    def test_append_field_kwargs(self):
-        int_field_class = bt2.IntegerFieldClass(32)
-        self._fc.append_field(name='int32', field_class=int_field_class)
+    def test_append_elemenbt_kwargs(self):
+        int_field_class = self._tc.create_signed_integer_field_class(32)
+        self._append_element_method(self._fc, name='int32', field_class=int_field_class)
         field_class = self._fc['int32']
-        self.assertEqual(field_class, int_field_class)
+        self.assertEqual(field_class.addr, int_field_class.addr)
+
+    def test_append_element_invalid_name(self):
+        sub_fc = self._tc.create_string_field_class()
 
-    def test_append_field_invalid_name(self):
         with self.assertRaises(TypeError):
-            self._fc.append_field(23, bt2.StringFieldClass())
+            self._append_element_method(self._fc, 23, sub_fc)
 
-    def test_append_field_invalid_field_class(self):
+    def test_append_element_invalid_field_class(self):
         with self.assertRaises(TypeError):
-            self._fc.append_field('yes', object())
+            self._append_element_method(self._fc, 'yes', object())
 
     def test_iadd(self):
-        struct_fc = bt2.StructureFieldClass()
-        c_field_class = bt2.StringFieldClass()
-        d_field_class = bt2.EnumerationFieldClass(size=32)
-        e_field_class = bt2.StructureFieldClass()
-        struct_fc.append_field('c_string', c_field_class)
-        struct_fc.append_field('d_enum', d_field_class)
-        struct_fc.append_field('e_struct', e_field_class)
-        a_field_class = bt2.FloatingPointNumberFieldClass()
-        b_field_class = bt2.IntegerFieldClass(17)
-        self._fc.append_field('a_float', a_field_class)
-        self._fc.append_field('b_int', b_field_class)
+        struct_fc = self._tc.create_structure_field_class()
+        c_field_class = self._tc.create_string_field_class()
+        d_field_class = self._tc.create_signed_enumeration_field_class(field_value_range=32)
+        e_field_class = self._tc.create_structure_field_class()
+        self._append_element_method(struct_fc, 'c_string', c_field_class)
+        self._append_element_method(struct_fc, 'd_enum', d_field_class)
+        self._append_element_method(struct_fc, 'e_struct', e_field_class)
+        a_field_class = self._tc.create_real_field_class()
+        b_field_class = self._tc.create_signed_integer_field_class(17)
+        self._append_element_method(self._fc, 'a_float', a_field_class)
+        self._append_element_method(self._fc, 'b_int', b_field_class)
         self._fc += struct_fc
-        self.assertEqual(self._fc['a_float'], a_field_class)
-        self.assertEqual(self._fc['b_int'], b_field_class)
-        self.assertEqual(self._fc['c_string'], c_field_class)
-        self.assertEqual(self._fc['d_enum'], d_field_class)
-        self.assertEqual(self._fc['e_struct'], e_field_class)
+        self.assertEqual(self._fc['a_float'].addr, a_field_class.addr)
+        self.assertEqual(self._fc['b_int'].addr, b_field_class.addr)
+        self.assertEqual(self._fc['c_string'].addr, c_field_class.addr)
+        self.assertEqual(self._fc['d_enum'].addr, d_field_class.addr)
+        self.assertEqual(self._fc['e_struct'].addr, e_field_class.addr)
 
     def test_bool_op(self):
         self.assertFalse(self._fc)
-        self._fc.append_field('a', bt2.StringFieldClass())
+        self._append_element_method(self._fc, 'a', self._tc.create_string_field_class())
         self.assertTrue(self._fc)
 
     def test_len(self):
-        fc = bt2.StringFieldClass()
-        self._fc.append_field('a', fc)
-        self._fc.append_field('b', fc)
-        self._fc.append_field('c', fc)
+        fc = self._tc.create_string_field_class()
+        self._append_element_method(self._fc, 'a', fc)
+        self._append_element_method(self._fc, 'b', fc)
+        self._append_element_method(self._fc, 'c', fc)
         self.assertEqual(len(self._fc), 3)
 
     def test_getitem(self):
-        a_fc = bt2.IntegerFieldClass(32)
-        b_fc = bt2.StringFieldClass()
-        c_fc = bt2.FloatingPointNumberFieldClass()
-        self._fc.append_field('a', a_fc)
-        self._fc.append_field('b', b_fc)
-        self._fc.append_field('c', c_fc)
-        self.assertEqual(self._fc['b'], b_fc)
+        a_fc = self._tc.create_signed_integer_field_class(32)
+        b_fc = self._tc.create_string_field_class()
+        c_fc = self._tc.create_real_field_class()
+        self._append_element_method(self._fc, 'a', a_fc)
+        self._append_element_method(self._fc, 'b', b_fc)
+        self._append_element_method(self._fc, 'c', c_fc)
+        self.assertEqual(self._fc['b'].addr, b_fc.addr)
 
     def test_getitem_invalid_key_type(self):
         with self.assertRaises(TypeError):
@@ -533,13 +325,13 @@ class _TestFieldContainer(_TestInvalidEq, _TestCopySimple):
 
     def test_contains(self):
         self.assertFalse('a' in self._fc)
-        self._fc.append_field('a', bt2.StringFieldClass())
+        self._append_element_method(self._fc, 'a', self._tc.create_string_field_class())
         self.assertTrue('a' in self._fc)
 
     def test_iter(self):
-        a_fc = bt2.IntegerFieldClass(32)
-        b_fc = bt2.StringFieldClass()
-        c_fc = bt2.FloatingPointNumberFieldClass()
+        a_fc = self._tc.create_signed_integer_field_class(32)
+        b_fc = self._tc.create_string_field_class()
+        c_fc = self._tc.create_real_field_class()
         fields = (
             ('a', a_fc),
             ('b', b_fc),
@@ -547,161 +339,228 @@ class _TestFieldContainer(_TestInvalidEq, _TestCopySimple):
         )
 
         for field in fields:
-            self._fc.append_field(*field)
+            self._append_element_method(self._fc, *field)
 
         for (name, fc_field_class), field in zip(self._fc.items(), fields):
             self.assertEqual(name, field[0])
-            self.assertEqual(fc_field_class, field[1])
+            self.assertEqual(fc_field_class.addr, field[1].addr)
 
     def test_at_index(self):
-        a_fc = bt2.IntegerFieldClass(32)
-        b_fc = bt2.StringFieldClass()
-        c_fc = bt2.FloatingPointNumberFieldClass()
-        self._fc.append_field('c', c_fc)
-        self._fc.append_field('a', a_fc)
-        self._fc.append_field('b', b_fc)
-        self.assertEqual(self._fc.at_index(1), a_fc)
+        a_fc = self._tc.create_signed_integer_field_class(32)
+        b_fc = self._tc.create_string_field_class()
+        c_fc = self._tc.create_real_field_class()
+        self._append_element_method(self._fc, 'c', c_fc)
+        self._append_element_method(self._fc, 'a', a_fc)
+        self._append_element_method(self._fc, 'b', b_fc)
+        self.assertEqual(self._at_index_method(self._fc, 1).addr, a_fc.addr)
 
     def test_at_index_invalid(self):
-        self._fc.append_field('c', bt2.IntegerFieldClass(32))
+        self._append_element_method(self._fc, 'c', self._tc.create_signed_integer_field_class(32))
 
         with self.assertRaises(TypeError):
-            self._fc.at_index('yes')
+            self._at_index_method(self._fc, 'yes')
 
     def test_at_index_out_of_bounds_after(self):
-        self._fc.append_field('c', bt2.IntegerFieldClass(32))
+        self._append_element_method(self._fc, 'c', self._tc.create_signed_integer_field_class(32))
 
         with self.assertRaises(IndexError):
-            self._fc.at_index(len(self._fc))
+            self._at_index_method(self._fc, len(self._fc))
 
 
-@unittest.skip("this is broken")
 class StructureFieldClassTestCase(_TestFieldContainer, unittest.TestCase):
     def setUp(self):
-        self._fc = bt2.StructureFieldClass()
+        self._append_element_method = bt2.field_class._StructureFieldClass.append_member
+        self._at_index_method = bt2.field_class._StructureFieldClass.member_at_index
+        self._tc = get_default_trace_class()
+        self._fc = self._tc.create_structure_field_class()
+
+    def test_create_default(self):
+        self.assertIsNotNone(self._fc)
 
-    def tearDown(self):
-        del self._fc
+
+class VariantFieldClassTestCase(_TestFieldContainer, unittest.TestCase):
+    def setUp(self):
+        self._append_element_method = bt2.field_class._VariantFieldClass.append_option
+        self._at_index_method = bt2.field_class._VariantFieldClass.option_at_index
+        self._tc = get_default_trace_class()
+        self._fc = self._tc.create_variant_field_class()
 
     def test_create_default(self):
-        self.assertEqual(self._fc.alignment, 1)
+        fc = self._tc.create_variant_field_class()
 
-    def test_create_with_min_alignment(self):
-        fc = bt2.StructureFieldClass(8)
-        self.assertEqual(fc.alignment, 8)
+        self.assertIsNone(fc.selector_field_path)
 
-    def test_assign_alignment(self):
-        with self.assertRaises(AttributeError):
-            self._fc.alignment = 32
+    def _create_field_class_for_field_path_test(self):
+        # Create something equivalent to:
+        #
+        # struct outer_struct_fc {
+        #   real foo;
+        #   struct inner_struct_fc {
+        #     enum { first = 1, second = 2..434 } selector;
+        #     string bar;
+        #     string baz;
+        #     variant<selector> {
+        #       real a;
+        #       int21_t b;
+        #       uint34_t c;
+        #     } variant;
+        #   } inner_struct[2];
+        # };
+        selector_fc = self._tc.create_unsigned_enumeration_field_class(field_value_range=42)
+        selector_fc.map_range('first', 1)
+        selector_fc.map_range('second', 2, 434)
 
-    def test_assign_min_alignment(self):
-        self._fc.min_alignment = 64
-        self.assertTrue(self._fc.alignment >= 64)
+        fc = self._tc.create_variant_field_class(selector_fc)
+        fc.append_option('a', self._tc.create_real_field_class())
+        fc.append_option('b', self._tc.create_signed_integer_field_class(21))
+        fc.append_option('c', self._tc.create_unsigned_integer_field_class(34))
 
-    def test_assign_invalid_min_alignment(self):
-        with self.assertRaises(ValueError):
-            self._fc.min_alignment = 23
+        foo_fc = self._tc.create_real_field_class()
+        bar_fc = self._tc.create_string_field_class()
+        baz_fc = self._tc.create_string_field_class()
 
-    def test_assign_get_min_alignment(self):
-        with self.assertRaises(AttributeError):
-            self._fc.min_alignment
+        inner_struct_fc = self._tc.create_structure_field_class()
+        inner_struct_fc.append_member('selector', selector_fc)
+        inner_struct_fc.append_member('bar', bar_fc)
+        inner_struct_fc.append_member('baz', baz_fc)
+        inner_struct_fc.append_member('variant', fc)
 
-    def test_create_field(self):
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._StructureField)
+        inner_struct_array_fc = self._tc.create_static_array_field_class(inner_struct_fc, 2)
 
-    def test_create_field_init_invalid(self):
-        with self.assertRaises(bt2.Error):
-            field = self._fc(23)
+        outer_struct_fc = self._tc.create_structure_field_class()
+        outer_struct_fc.append_member('foo', foo_fc)
+        outer_struct_fc.append_member('inner_struct', inner_struct_array_fc)
 
+        # The path to the selector field is resolved when the sequence is
+        # actually used, for example in a packet context.
+        self._tc.create_stream_class(packet_context_field_class=outer_struct_fc)
 
-@unittest.skip("this is broken")
-class VariantFieldClassTestCase(_TestFieldContainer, unittest.TestCase):
-    def setUp(self):
-        self._fc = bt2.VariantFieldClass('path.to.tag')
+        return fc
 
-    def tearDown(self):
-        del self._fc
+    def test_selector_field_path_length(self):
+        fc = self._create_field_class_for_field_path_test()
+        self.assertEqual(len(fc.selector_field_path), 3)
 
-    def test_create_default(self):
-        self.assertEqual(self._fc.tag_name, 'path.to.tag')
+    def test_selector_field_path_iter(self):
+        fc = self._create_field_class_for_field_path_test()
+        path_items = list(fc.selector_field_path)
 
-    def test_create_invalid_tag_name(self):
-        with self.assertRaises(TypeError):
-            self._fc = bt2.VariantFieldClass(23)
+        self.assertEqual(len(path_items), 3)
 
-    def test_assign_tag_name(self):
-        self._fc.tag_name = 'a.different.tag'
-        self.assertEqual(self._fc.tag_name, 'a.different.tag')
+        self.assertIsInstance(path_items[0], bt2.field_path._IndexFieldPathItem)
+        self.assertEqual(path_items[0].index, 1)
 
-    def test_assign_invalid_tag_name(self):
-        with self.assertRaises(TypeError):
-            self._fc.tag_name = -17
+        self.assertIsInstance(path_items[1], bt2.field_path._CurrentArrayElementFieldPathItem)
 
+        self.assertIsInstance(path_items[2], bt2.field_path._IndexFieldPathItem)
+        self.assertEqual(path_items[2].index, 0)
 
-@unittest.skip("this is broken")
-class ArrayFieldClassTestCase(_TestInvalidEq, _TestCopySimple,
-                             unittest.TestCase):
-    def setUp(self):
-        self._elem_fc = bt2.IntegerFieldClass(23)
-        self._fc = bt2.ArrayFieldClass(self._elem_fc, 45)
+    def test_selector_field_path_root_scope(self):
+        fc = self._create_field_class_for_field_path_test()
+        self.assertEqual(fc.selector_field_path.root_scope, bt2.field_path.Scope.PACKET_CONTEXT)
 
-    def tearDown(self):
-        del self._fc
-        del self._elem_fc
+
+class StaticArrayFieldClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self._tc = get_default_trace_class()
+        self._elem_fc = self._tc.create_signed_integer_field_class(23)
 
     def test_create_default(self):
-        self.assertEqual(self._fc.element_field_class, self._elem_fc)
-        self.assertEqual(self._fc.length, 45)
+        fc = self._tc.create_static_array_field_class(self._elem_fc, 45)
+        self.assertEqual(fc.element_field_class.addr, self._elem_fc.addr)
+        self.assertEqual(fc.length, 45)
 
-    def test_create_invalid_field_class(self):
+    def test_create_invalid_elem_field_class(self):
         with self.assertRaises(TypeError):
-            self._fc = bt2.ArrayFieldClass(object(), 45)
+            self._tc.create_static_array_field_class(object(), 45)
 
     def test_create_invalid_length(self):
         with self.assertRaises(ValueError):
-            self._fc = bt2.ArrayFieldClass(bt2.StringFieldClass(), -17)
+            self._tc.create_static_array_field_class(self._tc.create_string_field_class(), -17)
 
     def test_create_invalid_length_type(self):
         with self.assertRaises(TypeError):
-            self._fc = bt2.ArrayFieldClass(bt2.StringFieldClass(), 'the length')
+            self._tc.create_static_array_field_class(self._tc.create_string_field_class(), 'the length')
+
+
+class DynamicArrayFieldClassTestCase(unittest.TestCase):
+    def setUp(self):
+        self._tc = get_default_trace_class()
+        self._elem_fc = self._tc.create_signed_integer_field_class(23)
+        self._len_fc = self._tc.create_unsigned_integer_field_class(12)
 
-    def test_create_field(self):
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._ArrayField)
+    def test_create_default(self):
+        fc = self._tc.create_dynamic_array_field_class(self._elem_fc)
+        self.assertEqual(fc.element_field_class.addr, self._elem_fc.addr)
+        self.assertIsNone(fc.length_field_path, None)
 
-    def test_create_field_init_invalid(self):
-        with self.assertRaises(bt2.Error):
-            field = self._fc(23)
+    def _create_field_class_for_field_path_test(self):
+        # Create something a field class that is equivalent to:
+        #
+        # struct outer_struct_fc {
+        #   real foo;
+        #   struct inner_struct_fc {
+        #     string bar;
+        #     string baz;
+        #     uint12_t len;
+        #     uint23_t dyn_array[len];
+        #   } inner_struct[2];
+        # };
 
+        fc = self._tc.create_dynamic_array_field_class(self._elem_fc, self._len_fc)
 
-@unittest.skip("this is broken")
-class SequenceFieldClassTestCase(_TestInvalidEq, _TestCopySimple,
-                                unittest.TestCase):
-    def setUp(self):
-        self._elem_fc = bt2.IntegerFieldClass(23)
-        self._fc = bt2.SequenceFieldClass(self._elem_fc, 'the.length')
+        foo_fc = self._tc.create_real_field_class()
+        bar_fc = self._tc.create_string_field_class()
+        baz_fc = self._tc.create_string_field_class()
 
-    def tearDown(self):
-        del self._fc
-        del self._elem_fc
+        inner_struct_fc = self._tc.create_structure_field_class()
+        inner_struct_fc.append_member('bar', bar_fc)
+        inner_struct_fc.append_member('baz', baz_fc)
+        inner_struct_fc.append_member('len', self._len_fc)
+        inner_struct_fc.append_member('dyn_array', fc)
 
-    def test_create_default(self):
-        self.assertEqual(self._fc.element_field_class, self._elem_fc)
-        self.assertEqual(self._fc.length_name, 'the.length')
+        inner_struct_array_fc = self._tc.create_static_array_field_class(inner_struct_fc, 2)
+
+        outer_struct_fc = self._tc.create_structure_field_class()
+        outer_struct_fc.append_member('foo', foo_fc)
+        outer_struct_fc.append_member('inner_struct', inner_struct_array_fc)
+
+        # The path to the length field is resolved when the sequence is
+        # actually used, for example in a packet context.
+        self._tc.create_stream_class(packet_context_field_class=outer_struct_fc)
+
+        return fc
+
+    def test_field_path_len(self):
+        fc = self._create_field_class_for_field_path_test()
+        self.assertEqual(len(fc.length_field_path), 3)
+
+    def test_field_path_iter(self):
+        fc = self._create_field_class_for_field_path_test()
+        path_items = list(fc.length_field_path)
+
+        self.assertEqual(len(path_items), 3)
+
+        self.assertIsInstance(path_items[0], bt2.field_path._IndexFieldPathItem)
+        self.assertEqual(path_items[0].index, 1)
+
+        self.assertIsInstance(path_items[1], bt2.field_path._CurrentArrayElementFieldPathItem)
+
+        self.assertIsInstance(path_items[2], bt2.field_path._IndexFieldPathItem)
+        self.assertEqual(path_items[2].index, 2)
+
+    def test_field_path_root_scope(self):
+        fc = self._create_field_class_for_field_path_test()
+        self.assertEqual(fc.length_field_path.root_scope, bt2.field_path.Scope.PACKET_CONTEXT)
 
     def test_create_invalid_field_class(self):
         with self.assertRaises(TypeError):
-            self._fc = bt2.ArrayFieldClass(object(), 'the.length')
+            self._tc.create_dynamic_array_field_class(object())
 
     def test_create_invalid_length_type(self):
         with self.assertRaises(TypeError):
-            self._fc = bt2.SequenceFieldClass(bt2.StringFieldClass(), 17)
+            self._tc.create_dynamic_array_field_class(self._tc.create_string_field_class(), 17)
 
-    def test_create_field(self):
-        field = self._fc()
-        self.assertIsInstance(field, bt2.field._SequenceField)
 
-    def test_create_field_init_invalid(self):
-        with self.assertRaises(bt2.Error):
-            field = self._fc(23)
+if __name__ == "__main__":
+    unittest.main()
This page took 0.050876 seconds and 4 git commands to generate.