From e42e1587604ec69593e13503e623688fe26ba1d1 Mon Sep 17 00:00:00 2001 From: Francis Deslauriers Date: Tue, 27 Aug 2019 12:14:45 -0400 Subject: [PATCH] bt2: Add `*ValueConst` classes and adapt tests Split Python Value classes to mimic the type safety offered by the C api. Const classes offer a read-only view of the data. Non-Const classes subclass their respective Const classes. Signed-off-by: Francis Deslauriers Change-Id: I479db305a281063c7ee8aee2890c33c0b22e7153 Reviewed-on: https://review.lttng.org/c/babeltrace/+/1983 Tested-by: jenkins Reviewed-by: Simon Marchi --- src/bindings/python/bt2/bt2/__init__.py | 8 + src/bindings/python/bt2/bt2/component.py | 6 +- src/bindings/python/bt2/bt2/query_executor.py | 2 +- src/bindings/python/bt2/bt2/value.py | 282 ++++++++++++------ tests/bindings/python/bt2/test_package.py | 24 ++ .../python/bt2/test_query_executor.py | 2 + tests/bindings/python/bt2/test_value.py | 144 ++++++++- 7 files changed, 364 insertions(+), 104 deletions(-) diff --git a/src/bindings/python/bt2/bt2/__init__.py b/src/bindings/python/bt2/bt2/__init__.py index 137fc423..9ea77fb8 100644 --- a/src/bindings/python/bt2/bt2/__init__.py +++ b/src/bindings/python/bt2/bt2/__init__.py @@ -121,6 +121,14 @@ from bt2.value import RealValue from bt2.value import StringValue from bt2.value import ArrayValue from bt2.value import MapValue +from bt2.value import _BoolValueConst +from bt2.value import _IntegerValueConst +from bt2.value import _UnsignedIntegerValueConst +from bt2.value import _SignedIntegerValueConst +from bt2.value import _RealValueConst +from bt2.value import _StringValueConst +from bt2.value import _ArrayValueConst +from bt2.value import _MapValueConst from bt2.version import __version__ diff --git a/src/bindings/python/bt2/bt2/component.py b/src/bindings/python/bt2/bt2/component.py index a4f80c61..acc5b22c 100644 --- a/src/bindings/python/bt2/bt2/component.py +++ b/src/bindings/python/bt2/bt2/component.py @@ -515,7 +515,7 @@ class _UserComponentType(type): # call user's __init__() method if params_ptr is not None: - params = bt2_value._create_from_ptr_and_get_ref(params_ptr) + params = bt2_value._create_from_const_ptr_and_get_ref(params_ptr) else: params = None @@ -574,7 +574,7 @@ class _UserComponentType(type): def _bt_get_supported_mip_versions_from_native(cls, params_ptr, obj, log_level): # this can raise, but the native side checks the exception if params_ptr is not None: - params = bt2_value._create_from_ptr_and_get_ref(params_ptr) + params = bt2_value._create_from_const_ptr_and_get_ref(params_ptr) else: params = None @@ -595,7 +595,7 @@ class _UserComponentType(type): def _bt_query_from_native(cls, priv_query_exec_ptr, object, params_ptr, method_obj): # this can raise, but the native side checks the exception if params_ptr is not None: - params = bt2_value._create_from_ptr_and_get_ref(params_ptr) + params = bt2_value._create_from_const_ptr_and_get_ref(params_ptr) else: params = None diff --git a/src/bindings/python/bt2/bt2/query_executor.py b/src/bindings/python/bt2/bt2/query_executor.py index 8e51f825..e23fc96f 100644 --- a/src/bindings/python/bt2/bt2/query_executor.py +++ b/src/bindings/python/bt2/bt2/query_executor.py @@ -120,7 +120,7 @@ class QueryExecutor(object._SharedObject, _QueryExecutorCommon): status, result_ptr = native_bt.query_executor_query(self._ptr) utils._handle_func_status(status, 'cannot query component class') assert result_ptr is not None - return bt2_value._create_from_ptr(result_ptr) + return bt2_value._create_from_const_ptr(result_ptr) class _PrivateQueryExecutor(_QueryExecutorCommon): diff --git a/src/bindings/python/bt2/bt2/value.py b/src/bindings/python/bt2/bt2/value.py index 3001502a..4c3afacb 100644 --- a/src/bindings/python/bt2/bt2/value.py +++ b/src/bindings/python/bt2/bt2/value.py @@ -29,7 +29,7 @@ import abc import bt2 -def _create_from_ptr(ptr): +def _create_from_ptr_template(ptr, object_map): if ptr is None: return @@ -41,15 +41,31 @@ def _create_from_ptr(ptr): return typeid = native_bt.value_get_type(ptr) - return _TYPE_TO_OBJ[typeid]._create_from_ptr(ptr) + return object_map[typeid]._create_from_ptr(ptr) -def _create_from_ptr_and_get_ref(ptr): +def _create_from_ptr(ptr): + return _create_from_ptr_template(ptr, _TYPE_TO_OBJ) + + +def _create_from_const_ptr(ptr): + return _create_from_ptr_template(ptr, _TYPE_TO_CONST_OBJ) + + +def _create_from_ptr_and_get_ref_template(ptr, object_map): if ptr is None or ptr == native_bt.value_null: return typeid = native_bt.value_get_type(ptr) - return _TYPE_TO_OBJ[typeid]._create_from_ptr_and_get_ref(ptr) + return object_map[typeid]._create_from_ptr_and_get_ref(ptr) + + +def _create_from_ptr_and_get_ref(ptr): + return _create_from_ptr_and_get_ref_template(ptr, _TYPE_TO_OBJ) + + +def _create_from_const_ptr_and_get_ref(ptr): + return _create_from_ptr_and_get_ref_template(ptr, _TYPE_TO_CONST_OBJ) def create_value(value): @@ -83,9 +99,13 @@ def create_value(value): ) -class _Value(object._SharedObject, metaclass=abc.ABCMeta): +class _ValueConst(object._SharedObject, metaclass=abc.ABCMeta): _get_ref = staticmethod(native_bt.value_get_ref) _put_ref = staticmethod(native_bt.value_put_ref) + _create_value_from_ptr = staticmethod(_create_from_const_ptr) + _create_value_from_ptr_and_get_ref = staticmethod( + _create_from_const_ptr_and_get_ref + ) def __ne__(self, other): return not (self == other) @@ -97,11 +117,16 @@ class _Value(object._SharedObject, metaclass=abc.ABCMeta): ) +class _Value(_ValueConst): + _create_value_from_ptr = staticmethod(_create_from_ptr) + _create_value_from_ptr_and_get_ref = staticmethod(_create_from_ptr_and_get_ref) + + @functools.total_ordering -class _NumericValue(_Value): +class _NumericValueConst(_ValueConst): @staticmethod def _extract_value(other): - if isinstance(other, BoolValue) or isinstance(other, bool): + if isinstance(other, _BoolValueConst) or isinstance(other, bool): return bool(other) if isinstance(other, numbers.Integral): @@ -196,7 +221,11 @@ class _NumericValue(_Value): return self._extract_value(base) ** self._value -class _IntegralValue(_NumericValue, numbers.Integral): +class _NumericValue(_NumericValueConst, _Value): + pass + + +class _IntegralValueConst(_NumericValueConst, numbers.Integral): def __lshift__(self, other): return self._value << self._extract_value(other) @@ -231,11 +260,26 @@ class _IntegralValue(_NumericValue, numbers.Integral): return ~self._value -class _RealValue(_NumericValue, numbers.Real): +class _IntegralValue(_IntegralValueConst, _NumericValue): pass -class BoolValue(_IntegralValue): +class _BoolValueConst(_IntegralValueConst): + _NAME = 'Const boolean' + + def __bool__(self): + return self._value + + def __repr__(self): + return repr(self._value) + + @property + def _value(self): + value = native_bt.value_bool_get(self._ptr) + return value != 0 + + +class BoolValue(_BoolValueConst, _IntegralValue): _NAME = 'Boolean' def __init__(self, value=None): @@ -247,37 +291,33 @@ class BoolValue(_IntegralValue): self._check_create_status(ptr) super().__init__(ptr) - def __bool__(self): - return self._value - - def __repr__(self): - return repr(self._value) - - def _value_to_bool(self, value): - if isinstance(value, BoolValue): + @classmethod + def _value_to_bool(cls, value): + if isinstance(value, _BoolValueConst): value = value._value if not isinstance(value, bool): raise TypeError( - "'{}' object is not a 'bool' or 'BoolValue' object".format( + "'{}' object is not a 'bool', 'BoolValue', or '_BoolValueConst' object".format( value.__class__ ) ) return value - @property - def _value(self): - value = native_bt.value_bool_get(self._ptr) - return value != 0 - def _set_value(self, value): native_bt.value_bool_set(self._ptr, self._value_to_bool(value)) value = property(fset=_set_value) -class _IntegerValue(_IntegralValue): +class _IntegerValueConst(_IntegralValueConst): + @property + def _value(self): + return self._get_value(self._ptr) + + +class _IntegerValue(_IntegerValueConst, _IntegralValue): def __init__(self, value=None): if value is None: ptr = self._create_default_value() @@ -287,41 +327,56 @@ class _IntegerValue(_IntegralValue): self._check_create_status(ptr) super().__init__(ptr) - def _value_to_int(self, value): + @classmethod + def _value_to_int(cls, value): if not isinstance(value, numbers.Integral): raise TypeError('expecting an integral number object') value = int(value) - self._check_int_range(value) + cls._check_int_range(value) return value - @property - def _value(self): - return self._get_value(self._ptr) - def _prop_set_value(self, value): self._set_value(self._ptr, self._value_to_int(value)) value = property(fset=_prop_set_value) -class UnsignedIntegerValue(_IntegerValue): +class _UnsignedIntegerValueConst(_IntegerValueConst): + _NAME = 'Const unsigned integer' + _get_value = staticmethod(native_bt.value_integer_unsigned_get) + + +class UnsignedIntegerValue(_UnsignedIntegerValueConst, _IntegerValue): + _NAME = 'Unsigned integer' _check_int_range = staticmethod(utils._check_uint64) _create_default_value = staticmethod(native_bt.value_integer_unsigned_create) _create_value = staticmethod(native_bt.value_integer_unsigned_create_init) _set_value = staticmethod(native_bt.value_integer_unsigned_set) - _get_value = staticmethod(native_bt.value_integer_unsigned_get) -class SignedIntegerValue(_IntegerValue): +class _SignedIntegerValueConst(_IntegerValueConst): + _NAME = 'Const signed integer' + _get_value = staticmethod(native_bt.value_integer_signed_get) + + +class SignedIntegerValue(_SignedIntegerValueConst, _IntegerValue): + _NAME = 'Signed integer' _check_int_range = staticmethod(utils._check_int64) _create_default_value = staticmethod(native_bt.value_integer_signed_create) _create_value = staticmethod(native_bt.value_integer_signed_create_init) _set_value = staticmethod(native_bt.value_integer_signed_set) - _get_value = staticmethod(native_bt.value_integer_signed_get) -class RealValue(_RealValue): +class _RealValueConst(_NumericValueConst, numbers.Real): + _NAME = 'Const real number' + + @property + def _value(self): + return native_bt.value_real_get(self._ptr) + + +class RealValue(_RealValueConst, _NumericValue): _NAME = 'Real number' def __init__(self, value=None): @@ -334,16 +389,13 @@ class RealValue(_RealValue): self._check_create_status(ptr) super().__init__(ptr) - def _value_to_float(self, value): + @classmethod + def _value_to_float(cls, value): if not isinstance(value, numbers.Real): raise TypeError("expecting a real number object") return float(value) - @property - def _value(self): - return native_bt.value_real_get(self._ptr) - def _set_value(self, value): native_bt.value_real_set(self._ptr, self._value_to_float(value)) @@ -351,20 +403,12 @@ class RealValue(_RealValue): @functools.total_ordering -class StringValue(collections.abc.Sequence, _Value): - _NAME = 'String' +class _StringValueConst(collections.abc.Sequence, _Value): + _NAME = 'Const string' - def __init__(self, value=None): - if value is None: - ptr = native_bt.value_string_create() - else: - ptr = native_bt.value_string_create_init(self._value_to_str(value)) - - self._check_create_status(ptr) - super().__init__(ptr) - - def _value_to_str(self, value): - if isinstance(value, self.__class__): + @classmethod + def _value_to_str(cls, value): + if isinstance(value, _StringValueConst): value = value._value utils._check_str(value) @@ -374,12 +418,6 @@ class StringValue(collections.abc.Sequence, _Value): def _value(self): return native_bt.value_string_get(self._ptr) - def _set_value(self, value): - status = native_bt.value_string_set(self._ptr, self._value_to_str(value)) - utils._handle_func_status(status) - - value = property(fset=_set_value) - def __eq__(self, other): try: return self._value == self._value_to_str(other) @@ -407,6 +445,25 @@ class StringValue(collections.abc.Sequence, _Value): def __contains__(self, item): return self._value_to_str(item) in self._value + +class StringValue(_StringValueConst, _Value): + _NAME = 'String' + + def __init__(self, value=None): + if value is None: + ptr = native_bt.value_string_create() + else: + ptr = native_bt.value_string_create_init(self._value_to_str(value)) + + self._check_create_status(ptr) + super().__init__(ptr) + + def _set_value(self, value): + status = native_bt.value_string_set(self._ptr, self._value_to_str(value)) + utils._handle_func_status(status) + + value = property(fset=_set_value) + def __iadd__(self, value): curvalue = self._value curvalue += self._value_to_str(value) @@ -414,27 +471,22 @@ class StringValue(collections.abc.Sequence, _Value): return self -class _Container: +class _ContainerConst: def __bool__(self): return len(self) != 0 + +class _Container(_ContainerConst): def __delitem__(self, index): raise NotImplementedError -class ArrayValue(_Container, collections.abc.MutableSequence, _Value): - _NAME = 'Array' - - def __init__(self, value=None): - ptr = native_bt.value_array_create() - self._check_create_status(ptr) - super().__init__(ptr) - - # Python will raise a TypeError if there's anything wrong with - # the iterable protocol. - if value is not None: - for elem in value: - self.append(elem) +class _ArrayValueConst(_ContainerConst, collections.abc.Sequence, _ValueConst): + _NAME = 'Const array' + _borrow_element_by_index = staticmethod( + native_bt.value_array_borrow_element_by_index_const + ) + _is_const = True def __eq__(self, other): if not isinstance(other, collections.abc.Sequence): @@ -471,9 +523,30 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): def __getitem__(self, index): self._check_index(index) - ptr = native_bt.value_array_borrow_element_by_index(self._ptr, index) + ptr = self._borrow_element_by_index(self._ptr, index) assert ptr - return _create_from_ptr_and_get_ref(ptr) + return self._create_value_from_ptr_and_get_ref(ptr) + + def __repr__(self): + return '[{}]'.format(', '.join([repr(v) for v in self])) + + +class ArrayValue(_ArrayValueConst, _Container, collections.abc.MutableSequence, _Value): + _NAME = 'Array' + _borrow_element_by_index = staticmethod( + native_bt.value_array_borrow_element_by_index + ) + + def __init__(self, value=None): + ptr = native_bt.value_array_create() + self._check_create_status(ptr) + super().__init__(ptr) + + # Python will raise a TypeError if there's anything wrong with + # the iterable protocol. + if value is not None: + for elem in value: + self.append(elem) def __setitem__(self, index, value): self._check_index(index) @@ -506,9 +579,6 @@ class ArrayValue(_Container, collections.abc.MutableSequence, _Value): return self - def __repr__(self): - return '[{}]'.format(', '.join([repr(v) for v in self])) - def insert(self, value): raise NotImplementedError @@ -533,19 +603,9 @@ class _MapValueKeyIterator(collections.abc.Iterator): return str(key) -class MapValue(_Container, collections.abc.MutableMapping, _Value): - _NAME = 'Map' - - def __init__(self, value=None): - ptr = native_bt.value_map_create() - self._check_create_status(ptr) - super().__init__(ptr) - - # Python will raise a TypeError if there's anything wrong with - # the iterable/mapping protocol. - if value is not None: - for key, elem in value.items(): - self[key] = elem +class _MapValueConst(_ContainerConst, collections.abc.Mapping, _ValueConst): + _NAME = 'Const map' + _borrow_entry_value_ptr = staticmethod(native_bt.value_map_borrow_entry_value_const) def __ne__(self, other): return _Value.__ne__(self, other) @@ -585,13 +645,33 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): def __getitem__(self, key): self._check_key(key) - ptr = native_bt.value_map_borrow_entry_value(self._ptr, key) + ptr = self._borrow_entry_value_ptr(self._ptr, key) assert ptr - return _create_from_ptr_and_get_ref(ptr) + return self._create_value_from_ptr_and_get_ref(ptr) def __iter__(self): return _MapValueKeyIterator(self) + def __repr__(self): + items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] + return '{{{}}}'.format(', '.join(items)) + + +class MapValue(_MapValueConst, _Container, collections.abc.MutableMapping, _Value): + _NAME = 'Map' + _borrow_entry_value_ptr = staticmethod(native_bt.value_map_borrow_entry_value) + + def __init__(self, value=None): + ptr = native_bt.value_map_create() + self._check_create_status(ptr) + super().__init__(ptr) + + # Python will raise a TypeError if there's anything wrong with + # the iterable/mapping protocol. + if value is not None: + for key, elem in value.items(): + self[key] = elem + def __setitem__(self, key, value): self._check_key_type(key) value = create_value(value) @@ -604,10 +684,6 @@ class MapValue(_Container, collections.abc.MutableMapping, _Value): status = native_bt.value_map_insert_entry(self._ptr, key, ptr) utils._handle_func_status(status) - def __repr__(self): - items = ['{}: {}'.format(repr(k), repr(v)) for k, v in self.items()] - return '{{{}}}'.format(', '.join(items)) - _TYPE_TO_OBJ = { native_bt.VALUE_TYPE_BOOL: BoolValue, @@ -618,3 +694,13 @@ _TYPE_TO_OBJ = { native_bt.VALUE_TYPE_ARRAY: ArrayValue, native_bt.VALUE_TYPE_MAP: MapValue, } + +_TYPE_TO_CONST_OBJ = { + native_bt.VALUE_TYPE_BOOL: _BoolValueConst, + native_bt.VALUE_TYPE_UNSIGNED_INTEGER: _UnsignedIntegerValueConst, + native_bt.VALUE_TYPE_SIGNED_INTEGER: _SignedIntegerValueConst, + native_bt.VALUE_TYPE_REAL: _RealValueConst, + native_bt.VALUE_TYPE_STRING: _StringValueConst, + native_bt.VALUE_TYPE_ARRAY: _ArrayValueConst, + native_bt.VALUE_TYPE_MAP: _MapValueConst, +} diff --git a/tests/bindings/python/bt2/test_package.py b/tests/bindings/python/bt2/test_package.py index 71e792a4..21ffe084 100644 --- a/tests/bindings/python/bt2/test_package.py +++ b/tests/bindings/python/bt2/test_package.py @@ -318,5 +318,29 @@ class PackageTestCase(unittest.TestCase): def test_has_MapValue(self): self._assert_in_bt2('MapValue') + def test_has_BoolValueConst(self): + self._assert_in_bt2('_BoolValueConst') + + def test_has__IntegerValueConst(self): + self._assert_in_bt2('_IntegerValueConst') + + def test_has_UnsignedIntegerValueConst(self): + self._assert_in_bt2('_UnsignedIntegerValueConst') + + def test_has_SignedIntegerValueConst(self): + self._assert_in_bt2('_SignedIntegerValueConst') + + def test_has_RealValueConst(self): + self._assert_in_bt2('_RealValueConst') + + def test_has_StringValueConst(self): + self._assert_in_bt2('_StringValueConst') + + def test_has_ArrayValueConst(self): + self._assert_in_bt2('_ArrayValueConst') + + def test_has_MapValueConst(self): + self._assert_in_bt2('_MapValueConst') + def test_has___version__(self): self._assert_in_bt2('__version__') diff --git a/tests/bindings/python/bt2/test_query_executor.py b/tests/bindings/python/bt2/test_query_executor.py index 1ed63f1a..26e55461 100644 --- a/tests/bindings/python/bt2/test_query_executor.py +++ b/tests/bindings/python/bt2/test_query_executor.py @@ -41,6 +41,8 @@ class QueryExecutorTestCase(unittest.TestCase): } res = bt2.QueryExecutor(MySink, 'obj', params).query() + self.assertIs(type(res), bt2._MapValueConst) + self.assertIs(type(res['bt2']), bt2._StringValueConst) self.assertEqual(query_params, params) self.assertEqual(res, {'null': None, 'bt2': 'BT2'}) del query_params diff --git a/tests/bindings/python/bt2/test_value.py b/tests/bindings/python/bt2/test_value.py index fea749fc..b8ecfe55 100644 --- a/tests/bindings/python/bt2/test_value.py +++ b/tests/bindings/python/bt2/test_value.py @@ -1086,6 +1086,20 @@ class CreateValueFuncTestCase(unittest.TestCase): bt2.create_value(a) +def _create_const_value(value): + class MySink(bt2._UserSinkComponent): + def _user_consume(self): + pass + + @classmethod + def _user_query(cls, priv_query_exec, obj, params, method_obj): + nonlocal value + return {'my_value': value} + + res = bt2.QueryExecutor(MySink, 'obj', None).query() + return res['my_value'] + + class BoolValueTestCase(_TestNumericValue, unittest.TestCase): def setUp(self): self._f = bt2.BoolValue(False) @@ -1446,6 +1460,7 @@ class StringValueTestCase(_TestCopySimple, unittest.TestCase): def setUp(self): self._def_value = 'Hello, World!' self._def = bt2.StringValue(self._def_value) + self._def_const = _create_const_value(self._def_value) self._def_new_value = 'Yes!' def tearDown(self): @@ -1496,7 +1511,10 @@ class StringValueTestCase(_TestCopySimple, unittest.TestCase): def test_eq(self): self.assertEqual(self._def, self._def_value) - def test_eq(self): + def test_const_eq(self): + self.assertEqual(self._def_const, self._def_value) + + def test_eq_raw(self): self.assertNotEqual(self._def, 23) def test_lt_vstring(self): @@ -1556,12 +1574,22 @@ class StringValueTestCase(_TestCopySimple, unittest.TestCase): def test_getitem(self): self.assertEqual(self._def[5], self._def_value[5]) - def test_append_str(self): + def test_const_getitem(self): + self.assertEqual(self._def_const[5], self._def_value[5]) + + def test_iadd_str(self): to_append = 'meow meow meow' self._def += to_append self._def_value += to_append self.assertEqual(self._def, self._def_value) + def test_const_iadd_str(self): + to_append = 'meow meow meow' + with self.assertRaises(TypeError): + self._def_const += to_append + + self.assertEqual(self._def_const, self._def_value) + def test_append_vstr(self): to_append = 'meow meow meow' self._def += bt2.create_value(to_append) @@ -1573,6 +1601,7 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): def setUp(self): self._def_value = [None, False, True, -23, 0, 42, -42.4, 23.17, 'yes'] self._def = bt2.ArrayValue(copy.deepcopy(self._def_value)) + self._def_const = _create_const_value(copy.deepcopy(self._def_value)) def tearDown(self): del self._def @@ -1619,9 +1648,16 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): def test_eq_int(self): self.assertNotEqual(self._def, 23) + def test_const_eq(self): + a1 = _create_const_value([1, 2, 3]) + a2 = [1, 2, 3] + self.assertEqual(a1, a2) + def test_eq_diff_len(self): a1 = bt2.create_value([1, 2, 3]) a2 = bt2.create_value([1, 2]) + self.assertIs(type(a1), bt2.ArrayValue) + self.assertIs(type(a2), bt2.ArrayValue) self.assertNotEqual(a1, a2) def test_eq_diff_content_same_len(self): @@ -1655,6 +1691,10 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): self._def[2] = None self.assertIsNone(self._def[2]) + def test_setitem_none(self): + self._def[2] = None + self.assertIsNone(self._def[2]) + def test_setitem_index_wrong_type(self): with self._assert_type_error(): self._def['yes'] = 23 @@ -1667,6 +1707,10 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): with self.assertRaises(IndexError): self._def[len(self._def)] = 23 + def test_const_setitem(self): + with self.assertRaises(TypeError): + self._def_const[2] = 19 + def test_append_none(self): self._def.append(None) self.assertIsNone(self._def[len(self._def) - 1]) @@ -1676,6 +1720,10 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): self._def.append(raw) self.assertEqual(self._def[len(self._def) - 1], raw) + def test_const_append(self): + with self.assertRaises(AttributeError): + self._def_const.append(12194) + def test_append_vint(self): raw = 145 self._def.append(bt2.create_value(raw)) @@ -1695,6 +1743,10 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): self.assertEqual(self._def[len(self._def) - 2], raw[1]) self.assertEqual(self._def[len(self._def) - 1], raw[2]) + def test_const_iadd(self): + with self.assertRaises(TypeError): + self._def_const += 12194 + def test_iadd_unknown(self): class A: pass @@ -1713,6 +1765,31 @@ class ArrayValueTestCase(_TestCopySimple, unittest.TestCase): for velem, elem in zip(self._def, self._def_value): self.assertEqual(velem, elem) + def test_const_iter(self): + for velem, elem in zip(self._def_const, self._def_value): + self.assertEqual(velem, elem) + + def test_const_get_item(self): + item1 = self._def_const[0] + item2 = self._def_const[2] + item3 = self._def_const[5] + item4 = self._def_const[7] + item5 = self._def_const[8] + + self.assertEqual(item1, None) + + self.assertIs(type(item2), bt2._BoolValueConst) + self.assertEqual(item2, True) + + self.assertIs(type(item3), bt2._SignedIntegerValueConst) + self.assertEqual(item3, 42) + + self.assertIs(type(item4), bt2._RealValueConst) + self.assertEqual(item4, 23.17) + + self.assertIs(type(item5), bt2._StringValueConst) + self.assertEqual(item5, 'yes') + class MapValueTestCase(_TestCopySimple, unittest.TestCase): def setUp(self): @@ -1728,6 +1805,7 @@ class MapValueTestCase(_TestCopySimple, unittest.TestCase): 'str': 'yes', } self._def = bt2.MapValue(copy.deepcopy(self._def_value)) + self._def_const = _create_const_value(self._def_value) def tearDown(self): del self._def @@ -1763,6 +1841,11 @@ class MapValueTestCase(_TestCopySimple, unittest.TestCase): def test_len(self): self.assertEqual(len(self._def), len(self._def_value)) + def test_const_eq(self): + a1 = _create_const_value({'a': 1, 'b': 2, 'c': 3}) + a2 = {'a': 1, 'b': 2, 'c': 3} + self.assertEqual(a1, a2) + def test_eq_int(self): self.assertNotEqual(self._def, 23) @@ -1771,16 +1854,31 @@ class MapValueTestCase(_TestCopySimple, unittest.TestCase): a2 = bt2.create_value({'a': 1, 'b': 2}) self.assertNotEqual(a1, a2) + def test_const_eq_diff_len(self): + a1 = _create_const_value({'a': 1, 'b': 2, 'c': 3}) + a2 = _create_const_value({'a': 1, 'b': 2}) + self.assertNotEqual(a1, a2) + def test_eq_diff_content_same_len(self): a1 = bt2.create_value({'a': 1, 'b': 2, 'c': 3}) a2 = bt2.create_value({'a': 4, 'b': 2, 'c': 3}) self.assertNotEqual(a1, a2) + def test_const_eq_diff_content_same_len(self): + a1 = _create_const_value({'a': 1, 'b': 2, 'c': 3}) + a2 = _create_const_value({'a': 4, 'b': 2, 'c': 3}) + self.assertNotEqual(a1, a2) + def test_eq_same_content_diff_keys(self): a1 = bt2.create_value({'a': 1, 'b': 2, 'c': 3}) a2 = bt2.create_value({'a': 1, 'k': 2, 'c': 3}) self.assertNotEqual(a1, a2) + def test_const_eq_same_content_diff_keys(self): + a1 = _create_const_value({'a': 1, 'b': 2, 'c': 3}) + a2 = _create_const_value({'a': 1, 'k': 2, 'c': 3}) + self.assertNotEqual(a1, a2) + def test_eq_same_content_same_len(self): raw = {'3': 3, 'True': True, 'array': [1, 2.5, None, {'a': 17.6, 'b': None}]} a1 = bt2.MapValue(raw) @@ -1788,11 +1886,22 @@ class MapValueTestCase(_TestCopySimple, unittest.TestCase): self.assertEqual(a1, a2) self.assertEqual(a1, raw) + def test_const_eq_same_content_same_len(self): + raw = {'3': 3, 'True': True, 'array': [1, 2.5, None, {'a': 17.6, 'b': None}]} + a1 = _create_const_value(raw) + a2 = _create_const_value(copy.deepcopy(raw)) + self.assertEqual(a1, a2) + self.assertEqual(a1, raw) + def test_setitem_int(self): raw = 19 self._def['pos-int'] = raw self.assertEqual(self._def['pos-int'], raw) + def test_const_setitem_int(self): + with self.assertRaises(TypeError): + self._def_const['pos-int'] = 19 + def test_setitem_vint(self): raw = 19 self._def['pos-int'] = bt2.create_value(raw) @@ -1817,6 +1926,37 @@ class MapValueTestCase(_TestCopySimple, unittest.TestCase): val = self._def_value[vkey] self.assertEqual(vval, val) + def test_const_iter(self): + for vkey, vval in self._def_const.items(): + val = self._def_value[vkey] + self.assertEqual(vval, val) + + def test_get_item(self): + i = self._def['pos-float'] + self.assertIs(type(i), bt2.RealValue) + self.assertEqual(i, 23.17) + + def test_const_get_item(self): + item1 = self._def_const['none'] + item2 = self._def_const['true'] + item3 = self._def_const['pos-int'] + item4 = self._def_const['pos-float'] + item5 = self._def_const['str'] + + self.assertEqual(item1, None) + + self.assertIs(type(item2), bt2._BoolValueConst) + self.assertEqual(item2, True) + + self.assertIs(type(item3), bt2._SignedIntegerValueConst) + self.assertEqual(item3, 42) + + self.assertIs(type(item4), bt2._RealValueConst) + self.assertEqual(item4, 23.17) + + self.assertIs(type(item5), bt2._StringValueConst) + self.assertEqual(item5, 'yes') + def test_getitem_wrong_key(self): with self.assertRaises(KeyError): self._def['kilojoule'] -- 2.34.1