]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - tests/test_pyderasn.py
Strict default values existence validation option
[pyderasn.git] / tests / test_pyderasn.py
index 0be393ed20841fc480345332e1864a6788ac9720..5b98ea60cf1012c829ef6b9d396ac245e858343b 100644 (file)
@@ -1,6 +1,6 @@
 # coding: utf-8
 # PyDERASN -- Python ASN.1 DER codec with abstract structures
-# Copyright (C) 2017 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as
@@ -50,12 +50,14 @@ from six import PY2
 from six import text_type
 
 from pyderasn import _pp
+from pyderasn import abs_decode_path
 from pyderasn import Any
 from pyderasn import BitString
 from pyderasn import BMPString
 from pyderasn import Boolean
 from pyderasn import BoundsError
 from pyderasn import Choice
+from pyderasn import decode_path_defby
 from pyderasn import DecodeError
 from pyderasn import Enumerated
 from pyderasn import GeneralizedTime
@@ -85,6 +87,7 @@ from pyderasn import SequenceOf
 from pyderasn import Set
 from pyderasn import SetOf
 from pyderasn import tag_ctxc
+from pyderasn import tag_ctxp
 from pyderasn import tag_decode
 from pyderasn import tag_encode
 from pyderasn import tag_strip
@@ -317,7 +320,7 @@ class CommonMixin(object):
 
 
 @composite
-def boolean_values_strat(draw, do_expl=False):
+def boolean_values_strategy(draw, do_expl=False):
     value = draw(one_of(none(), booleans()))
     impl = None
     expl = None
@@ -372,10 +375,12 @@ class TestBoolean(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == bool(obj2), value1 == value2)
             obj1 = klass(value1, impl=tag1)
             obj2 = klass(value1, impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(data_strategy())
     def test_call(self, d):
@@ -387,7 +392,7 @@ class TestBoolean(CommonMixin, TestCase):
                 default_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(boolean_values_strat())
+            ) = d.draw(boolean_values_strategy())
             obj_initial = klass(
                 value_initial,
                 impl_initial,
@@ -403,7 +408,7 @@ class TestBoolean(CommonMixin, TestCase):
                 default,
                 optional,
                 _decoded,
-            ) = d.draw(boolean_values_strat(do_expl=impl_initial is None))
+            ) = d.draw(boolean_values_strategy(do_expl=impl_initial is None))
             obj = obj_initial(value, impl, expl, default, optional)
             if obj.ready:
                 value_expected = default if value is None else value
@@ -425,7 +430,7 @@ class TestBoolean(CommonMixin, TestCase):
                 optional = True
             self.assertEqual(obj.optional, optional)
 
-    @given(boolean_values_strat())
+    @given(boolean_values_strategy())
     def test_copy(self, values):
         for klass in (Boolean, BooleanInherited):
             obj = klass(*values)
@@ -520,7 +525,7 @@ class TestBoolean(CommonMixin, TestCase):
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
-        boolean_values_strat(),
+        boolean_values_strategy(),
         booleans(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
@@ -586,7 +591,7 @@ class TestBoolean(CommonMixin, TestCase):
 
 
 @composite
-def integer_values_strat(draw, do_expl=False):
+def integer_values_strategy(draw, do_expl=False):
     bound_min, value, default, bound_max = sorted(draw(sets(
         integers(),
         min_size=4,
@@ -677,10 +682,12 @@ class TestInteger(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == int(obj2), value1 == value2)
             obj1 = klass(value1, impl=tag1)
             obj2 = klass(value1, impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(lists(integers()))
     def test_sorted_works(self, values):
@@ -734,7 +741,7 @@ class TestInteger(CommonMixin, TestCase):
                 optional_initial,
                 _specs_initial,
                 _decoded_initial,
-            ) = d.draw(integer_values_strat())
+            ) = d.draw(integer_values_strategy())
             obj_initial = klass(
                 value_initial,
                 bounds_initial,
@@ -754,7 +761,7 @@ class TestInteger(CommonMixin, TestCase):
                 optional,
                 _,
                 _decoded,
-            ) = d.draw(integer_values_strat(do_expl=impl_initial is None))
+            ) = d.draw(integer_values_strategy(do_expl=impl_initial is None))
             if (default is None) and (obj_initial.default is not None):
                 bounds = None
             if (
@@ -800,7 +807,7 @@ class TestInteger(CommonMixin, TestCase):
                 {} if _specs_initial is None else dict(_specs_initial),
             )
 
-    @given(integer_values_strat())
+    @given(integer_values_strategy())
     def test_copy(self, values):
         for klass in (Integer, IntegerInherited):
             obj = klass(*values)
@@ -893,7 +900,7 @@ class TestInteger(CommonMixin, TestCase):
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
-        integer_values_strat(),
+        integer_values_strategy(),
         integers(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
@@ -978,7 +985,7 @@ class TestInteger(CommonMixin, TestCase):
 
 
 @composite
-def bit_string_values_strat(draw, schema=None, value_required=False, do_expl=False):
+def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=False):
     if schema is None:
         schema = ()
         if draw(booleans()):
@@ -1083,7 +1090,7 @@ class TestBitString(CommonMixin, TestCase):
             BitString(b"whatever")["whenever"]
         repr(err.exception)
 
-    def test_get_invalid_typ(self):
+    def test_get_invalid_type(self):
         with self.assertRaises(InvalidValueType) as err:
             BitString(b"whatever")[(1, 2, 3)]
         repr(err.exception)
@@ -1129,10 +1136,12 @@ class TestBitString(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == bytes(obj2), value1[1] == value2[1])
             obj1 = klass(value1, impl=tag1)
             obj2 = klass(value1, impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(data_strategy())
     def test_call(self, d):
@@ -1145,7 +1154,7 @@ class TestBitString(CommonMixin, TestCase):
                 default_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(bit_string_values_strat())
+            ) = d.draw(bit_string_values_strategy())
 
             class BS(klass):
                 schema = schema_initial
@@ -1165,7 +1174,7 @@ class TestBitString(CommonMixin, TestCase):
                 default,
                 optional,
                 _decoded,
-            ) = d.draw(bit_string_values_strat(
+            ) = d.draw(bit_string_values_strategy(
                 schema=schema_initial,
                 do_expl=impl_initial is None,
             ))
@@ -1186,7 +1195,7 @@ class TestBitString(CommonMixin, TestCase):
             self.assertEqual(obj.optional, optional)
             self.assertEqual(obj.specs, obj_initial.specs)
 
-    @given(bit_string_values_strat())
+    @given(bit_string_values_strategy())
     def test_copy(self, values):
         for klass in (BitString, BitStringInherited):
             _schema, value, impl, expl, default, optional, _decoded = values
@@ -1269,7 +1278,7 @@ class TestBitString(CommonMixin, TestCase):
             default,
             optional,
             _decoded,
-        ) = d.draw(bit_string_values_strat(value_required=True))
+        ) = d.draw(bit_string_values_strategy(value_required=True))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         for klass in (BitString, BitStringInherited):
@@ -1365,7 +1374,7 @@ class TestBitString(CommonMixin, TestCase):
 
 
 @composite
-def octet_string_values_strat(draw, do_expl=False):
+def octet_string_values_strategy(draw, do_expl=False):
     bound_min, bound_max = sorted(draw(sets(
         integers(min_value=0, max_value=1 << 7),
         min_size=2,
@@ -1434,10 +1443,19 @@ class TestOctetString(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
             obj1 = klass(value1, impl=tag1)
             obj2 = klass(value1, impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
+
+    @given(lists(binary()))
+    def test_sorted_works(self, values):
+        self.assertSequenceEqual(
+            [bytes(v) for v in sorted(OctetString(v) for v in values)],
+            sorted(values),
+        )
 
     @given(data_strategy())
     def test_bounds_satisfied(self, d):
@@ -1470,7 +1488,7 @@ class TestOctetString(CommonMixin, TestCase):
                 default_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(octet_string_values_strat())
+            ) = d.draw(octet_string_values_strategy())
             obj_initial = klass(
                 value_initial,
                 bounds_initial,
@@ -1488,7 +1506,7 @@ class TestOctetString(CommonMixin, TestCase):
                 default,
                 optional,
                 _decoded,
-            ) = d.draw(octet_string_values_strat(do_expl=impl_initial is None))
+            ) = d.draw(octet_string_values_strategy(do_expl=impl_initial is None))
             if (default is None) and (obj_initial.default is not None):
                 bounds = None
             if (
@@ -1530,7 +1548,7 @@ class TestOctetString(CommonMixin, TestCase):
                 bounds or bounds_initial or (0, float("+inf")),
             )
 
-    @given(octet_string_values_strat())
+    @given(octet_string_values_strategy())
     def test_copy(self, values):
         for klass in (OctetString, OctetStringInherited):
             obj = klass(*values)
@@ -1615,7 +1633,7 @@ class TestOctetString(CommonMixin, TestCase):
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
-        octet_string_values_strat(),
+        octet_string_values_strategy(),
         binary(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
@@ -1663,7 +1681,7 @@ class TestOctetString(CommonMixin, TestCase):
 
 
 @composite
-def null_values_strat(draw, do_expl=False):
+def null_values_strategy(draw, do_expl=False):
     impl = None
     expl = None
     if do_expl:
@@ -1698,6 +1716,7 @@ class TestNull(CommonMixin, TestCase):
             obj1 = klass(impl=tag1)
             obj2 = klass(impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
             self.assertNotEqual(obj1, tag2)
 
     @given(data_strategy())
@@ -1708,7 +1727,7 @@ class TestNull(CommonMixin, TestCase):
                 expl_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(null_values_strat())
+            ) = d.draw(null_values_strategy())
             obj_initial = klass(
                 impl=impl_initial,
                 expl=expl_initial,
@@ -1720,7 +1739,7 @@ class TestNull(CommonMixin, TestCase):
                 expl,
                 optional,
                 _decoded,
-            ) = d.draw(null_values_strat(do_expl=impl_initial is None))
+            ) = d.draw(null_values_strategy(do_expl=impl_initial is None))
             obj = obj_initial(impl=impl, expl=expl, optional=optional)
             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
             self.assertEqual(obj.expl_tag, expl or expl_initial)
@@ -1728,7 +1747,7 @@ class TestNull(CommonMixin, TestCase):
             optional = False if optional is None else optional
             self.assertEqual(obj.optional, optional)
 
-    @given(null_values_strat())
+    @given(null_values_strategy())
     def test_copy(self, values):
         for klass in (Null, NullInherited):
             impl, expl, optional, _decoded = values
@@ -1794,7 +1813,7 @@ class TestNull(CommonMixin, TestCase):
             Null(impl=impl).decode(Null().encode())
 
     @given(
-        null_values_strat(),
+        null_values_strategy(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
     )
@@ -1854,7 +1873,7 @@ def oid_strategy(draw):
 
 
 @composite
-def oid_values_strat(draw, do_expl=False):
+def oid_values_strategy(draw, do_expl=False):
     value = draw(one_of(none(), oid_strategy()))
     impl = None
     expl = None
@@ -1910,11 +1929,13 @@ class TestObjectIdentifier(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == tuple(obj2), value1 == value2)
             self.assertEqual(str(obj1) == str(obj2), value1 == value2)
             obj1 = klass(value1, impl=tag1)
             obj2 = klass(value1, impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(lists(oid_strategy()))
     def test_sorted_works(self, values):
@@ -1933,14 +1954,14 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 default_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(oid_values_strat())
+            ) = d.draw(oid_values_strategy())
             obj_initial = klass(
-                value_initial,
-                impl_initial,
-                expl_initial,
-                default_initial,
-                optional_initial or False,
-                _decoded_initial,
+                value=value_initial,
+                impl=impl_initial,
+                expl=expl_initial,
+                default=default_initial,
+                optional=optional_initial or False,
+                _decoded=_decoded_initial,
             )
             (
                 value,
@@ -1949,8 +1970,14 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 default,
                 optional,
                 _decoded,
-            ) = d.draw(oid_values_strat(do_expl=impl_initial is None))
-            obj = obj_initial(value, impl, expl, default, optional)
+            ) = d.draw(oid_values_strategy(do_expl=impl_initial is None))
+            obj = obj_initial(
+                value=value,
+                impl=impl,
+                expl=expl,
+                default=default,
+                optional=optional,
+            )
             if obj.ready:
                 value_expected = default if value is None else value
                 value_expected = (
@@ -1971,10 +1998,25 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 optional = True
             self.assertEqual(obj.optional, optional)
 
-    @given(oid_values_strat())
+    @given(oid_values_strategy())
     def test_copy(self, values):
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
-            obj = klass(*values)
+            (
+                value,
+                impl,
+                expl,
+                default,
+                optional,
+                _decoded,
+            ) = values
+            obj = klass(
+                value=value,
+                impl=impl,
+                expl=expl,
+                default=default,
+                optional=optional,
+                _decoded=_decoded,
+            )
             obj_copied = obj.copy()
             self.assert_copied_basic_fields(obj, obj_copied)
             self.assertEqual(obj._value, obj_copied._value)
@@ -2090,7 +2132,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
-        oid_values_strat(),
+        oid_values_strategy(),
         oid_strategy(),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
@@ -2174,7 +2216,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
 
 
 @composite
-def enumerated_values_strat(draw, schema=None, do_expl=False):
+def enumerated_values_strategy(draw, schema=None, do_expl=False):
     if schema is None:
         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
         values = list(draw(sets(
@@ -2272,10 +2314,12 @@ class TestEnumerated(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == int(obj2), value1 == value2)
             obj1 = klass(value1, impl=tag1)
             obj2 = klass(value1, impl=tag2)
             self.assertEqual(obj1 == obj2, tag1 == tag2)
+            self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(data_strategy())
     def test_call(self, d):
@@ -2287,7 +2331,7 @@ class TestEnumerated(CommonMixin, TestCase):
             default_initial,
             optional_initial,
             _decoded_initial,
-        ) = d.draw(enumerated_values_strat())
+        ) = d.draw(enumerated_values_strategy())
 
         class E(Enumerated):
             schema = schema_initial
@@ -2307,7 +2351,7 @@ class TestEnumerated(CommonMixin, TestCase):
             default,
             optional,
             _decoded,
-        ) = d.draw(enumerated_values_strat(
+        ) = d.draw(enumerated_values_strategy(
             schema=schema_initial,
             do_expl=impl_initial is None,
         ))
@@ -2342,7 +2386,7 @@ class TestEnumerated(CommonMixin, TestCase):
         self.assertEqual(obj.optional, optional)
         self.assertEqual(obj.specs, dict(schema_initial))
 
-    @given(enumerated_values_strat())
+    @given(enumerated_values_strategy())
     def test_copy(self, values):
         schema_input, value, impl, expl, default, optional, _decoded = values
 
@@ -2364,7 +2408,7 @@ class TestEnumerated(CommonMixin, TestCase):
     @given(data_strategy())
     def test_symmetric(self, d):
         schema_input, _, _, _, default, optional, _decoded = d.draw(
-            enumerated_values_strat(),
+            enumerated_values_strategy(),
         )
         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
         offset = d.draw(integers(min_value=0))
@@ -2412,7 +2456,7 @@ class TestEnumerated(CommonMixin, TestCase):
 
 
 @composite
-def string_values_strat(draw, alphabet, do_expl=False):
+def string_values_strategy(draw, alphabet, do_expl=False):
     bound_min, bound_max = sorted(draw(sets(
         integers(min_value=0, max_value=1 << 7),
         min_size=2,
@@ -2486,11 +2530,13 @@ class StringMixin(object):
         obj1 = self.base_klass(value1)
         obj2 = self.base_klass(value2)
         self.assertEqual(obj1 == obj2, value1 == value2)
+        self.assertEqual(obj1 != obj2, value1 != value2)
         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
         obj1 = self.base_klass(value1, impl=tag1)
         obj2 = self.base_klass(value1, impl=tag2)
         self.assertEqual(obj1 == obj2, tag1 == tag2)
+        self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(data_strategy())
     def test_bounds_satisfied(self, d):
@@ -2526,7 +2572,7 @@ class StringMixin(object):
             default_initial,
             optional_initial,
             _decoded_initial,
-        ) = d.draw(string_values_strat(self.text_alphabet()))
+        ) = d.draw(string_values_strategy(self.text_alphabet()))
         obj_initial = self.base_klass(
             value_initial,
             bounds_initial,
@@ -2544,7 +2590,7 @@ class StringMixin(object):
             default,
             optional,
             _decoded,
-        ) = d.draw(string_values_strat(
+        ) = d.draw(string_values_strategy(
             self.text_alphabet(),
             do_expl=impl_initial is None,
         ))
@@ -2591,7 +2637,7 @@ class StringMixin(object):
 
     @given(data_strategy())
     def test_copy(self, d):
-        values = d.draw(string_values_strat(self.text_alphabet()))
+        values = d.draw(string_values_strategy(self.text_alphabet()))
         obj = self.base_klass(*values)
         obj_copied = obj.copy()
         self.assert_copied_basic_fields(obj, obj_copied)
@@ -2674,7 +2720,7 @@ class StringMixin(object):
 
     @given(data_strategy())
     def test_symmetric(self, d):
-        values = d.draw(string_values_strat(self.text_alphabet()))
+        values = d.draw(string_values_strategy(self.text_alphabet()))
         value = d.draw(text(alphabet=self.text_alphabet()))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
@@ -2765,7 +2811,7 @@ class TestBMPString(StringMixin, CommonMixin, TestCase):
 
 
 @composite
-def generalized_time_values_strat(
+def generalized_time_values_strategy(
         draw,
         min_datetime,
         max_datetime,
@@ -2846,11 +2892,13 @@ class TimeMixin(object):
         obj1 = self.base_klass(value1)
         obj2 = self.base_klass(value2)
         self.assertEqual(obj1 == obj2, value1 == value2)
+        self.assertEqual(obj1 != obj2, value1 != value2)
         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
         obj1 = self.base_klass(value1, impl=tag1)
         obj2 = self.base_klass(value1, impl=tag2)
         self.assertEqual(obj1 == obj2, tag1 == tag2)
+        self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(data_strategy())
     def test_call(self, d):
@@ -2861,7 +2909,7 @@ class TimeMixin(object):
             default_initial,
             optional_initial,
             _decoded_initial,
-        ) = d.draw(generalized_time_values_strat(
+        ) = d.draw(generalized_time_values_strategy(
             min_datetime=self.min_datetime,
             max_datetime=self.max_datetime,
             omit_ms=self.omit_ms,
@@ -2881,7 +2929,7 @@ class TimeMixin(object):
             default,
             optional,
             _decoded,
-        ) = d.draw(generalized_time_values_strat(
+        ) = d.draw(generalized_time_values_strategy(
             min_datetime=self.min_datetime,
             max_datetime=self.max_datetime,
             omit_ms=self.omit_ms,
@@ -2916,7 +2964,7 @@ class TimeMixin(object):
 
     @given(data_strategy())
     def test_copy(self, d):
-        values = d.draw(generalized_time_values_strat(
+        values = d.draw(generalized_time_values_strategy(
             min_datetime=self.min_datetime,
             max_datetime=self.max_datetime,
         ))
@@ -2950,7 +2998,7 @@ class TimeMixin(object):
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_symmetric(self, d):
-        values = d.draw(generalized_time_values_strat(
+        values = d.draw(generalized_time_values_strategy(
             min_datetime=self.min_datetime,
             max_datetime=self.max_datetime,
         ))
@@ -3105,7 +3153,7 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
 
 
 @composite
-def any_values_strat(draw, do_expl=False):
+def any_values_strategy(draw, do_expl=False):
     value = draw(one_of(none(), binary()))
     expl = None
     if do_expl:
@@ -3173,6 +3221,7 @@ class TestAny(CommonMixin, TestCase):
             obj1 = klass(value1)
             obj2 = klass(value2)
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
 
     @given(data_strategy())
@@ -3183,7 +3232,7 @@ class TestAny(CommonMixin, TestCase):
                 expl_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(any_values_strat())
+            ) = d.draw(any_values_strategy())
             obj_initial = klass(
                 value_initial,
                 expl_initial,
@@ -3195,7 +3244,7 @@ class TestAny(CommonMixin, TestCase):
                 expl,
                 optional,
                 _decoded,
-            ) = d.draw(any_values_strat(do_expl=True))
+            ) = d.draw(any_values_strategy(do_expl=True))
             obj = obj_initial(value, expl, optional)
             if obj.ready:
                 value_expected = None if value is None else value
@@ -3214,7 +3263,7 @@ class TestAny(CommonMixin, TestCase):
         # override it, as Any does not have implicit tag
         pass
 
-    @given(any_values_strat())
+    @given(any_values_strategy())
     def test_copy(self, values):
         for klass in (Any, AnyInherited):
             obj = klass(*values)
@@ -3273,7 +3322,7 @@ class TestAny(CommonMixin, TestCase):
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
-        any_values_strat(),
+        any_values_strategy(),
         integers().map(lambda x: Integer(x).encode()),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
@@ -3318,7 +3367,7 @@ class TestAny(CommonMixin, TestCase):
 
 
 @composite
-def choice_values_strat(draw, value_required=False, schema=None, do_expl=False):
+def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
     if schema is None:
         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
         tags = [tag_encode(tag) for tag in draw(sets(
@@ -3412,6 +3461,7 @@ class TestChoice(CommonMixin, TestCase):
             obj1 = klass(("whatever", Boolean(value1)))
             obj2 = klass(("whatever", Boolean(value2)))
             self.assertEqual(obj1 == obj2, value1 == value2)
+            self.assertEqual(obj1 != obj2, value1 != value2)
             self.assertEqual(obj1 == obj2._value, value1 == value2)
             self.assertFalse(obj1 == obj2._value[1])
 
@@ -3425,7 +3475,7 @@ class TestChoice(CommonMixin, TestCase):
                 default_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(choice_values_strat())
+            ) = d.draw(choice_values_strategy())
 
             class Wahl(klass):
                 schema = schema_initial
@@ -3443,7 +3493,7 @@ class TestChoice(CommonMixin, TestCase):
                 default,
                 optional,
                 _decoded,
-            ) = d.draw(choice_values_strat(schema=schema_initial, do_expl=True))
+            ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
             obj = obj_initial(value, expl, default, optional)
             if obj.ready:
                 value_expected = default if value is None else value
@@ -3474,7 +3524,7 @@ class TestChoice(CommonMixin, TestCase):
         # override it, as Any does not have implicit tag
         pass
 
-    @given(choice_values_strat())
+    @given(choice_values_strategy())
     def test_copy(self, values):
         _schema, value, expl, default, optional, _decoded = values
 
@@ -3516,7 +3566,7 @@ class TestChoice(CommonMixin, TestCase):
     @given(data_strategy())
     def test_symmetric(self, d):
         _schema, value, _, default, optional, _decoded = d.draw(
-            choice_values_strat(value_required=True)
+            choice_values_strategy(value_required=True)
         )
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
@@ -3603,7 +3653,7 @@ class TestChoice(CommonMixin, TestCase):
 
 
 @composite
-def seq_values_strat(draw, seq_klass, do_expl=False):
+def seq_values_strategy(draw, seq_klass, do_expl=False):
     value = None
     if draw(booleans()):
         value = seq_klass()
@@ -3653,7 +3703,7 @@ def seq_values_strat(draw, seq_klass, do_expl=False):
 
 
 @composite
-def sequence_strat(draw, seq_klass):
+def sequence_strategy(draw, seq_klass):
     inputs = draw(lists(
         one_of(
             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
@@ -3722,7 +3772,7 @@ def sequence_strat(draw, seq_klass):
 
 
 @composite
-def sequences_strat(draw, seq_klass):
+def sequences_strategy(draw, seq_klass):
     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
     inits = [
         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
@@ -3745,7 +3795,7 @@ def sequences_strat(draw, seq_klass):
         max_size=len(tags),
     )))
     seq_expectses = draw(lists(
-        sequence_strat(seq_klass=seq_klass),
+        sequence_strategy(seq_klass=seq_klass),
         min_size=len(tags),
         max_size=len(tags),
     ))
@@ -3850,7 +3900,7 @@ class SeqMixing(object):
                 default_initial,
                 optional_initial,
                 _decoded_initial,
-            ) = d.draw(seq_values_strat(seq_klass=klass))
+            ) = d.draw(seq_values_strategy(seq_klass=klass))
             obj_initial = klass(
                 value_initial,
                 schema_initial,
@@ -3868,7 +3918,7 @@ class SeqMixing(object):
                 default,
                 optional,
                 _decoded,
-            ) = d.draw(seq_values_strat(
+            ) = d.draw(seq_values_strategy(
                 seq_klass=klass,
                 do_expl=impl_initial is None,
             ))
@@ -3898,7 +3948,7 @@ class SeqMixing(object):
         class SeqInherited(self.base_klass):
             pass
         for klass in (self.base_klass, SeqInherited):
-            values = d.draw(seq_values_strat(seq_klass=klass))
+            values = d.draw(seq_values_strategy(seq_klass=klass))
             obj = klass(*values)
             obj_copied = obj.copy()
             self.assert_copied_basic_fields(obj, obj_copied)
@@ -4006,7 +4056,7 @@ class SeqMixing(object):
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_symmetric(self, d):
-        seq, expects = d.draw(sequence_strat(seq_klass=self.base_klass))
+        seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
         self.assertTrue(seq.ready)
         self.assertFalse(seq.decoded)
         self._assert_expects(seq, expects)
@@ -4036,7 +4086,7 @@ class SeqMixing(object):
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_symmetric_with_seq(self, d):
-        seq, expect_outers = d.draw(sequences_strat(seq_klass=self.base_klass))
+        seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
         self.assertTrue(seq.ready)
         seq_encoded = seq.encode()
         seq_decoded, tail = seq.decode(seq_encoded)
@@ -4203,7 +4253,7 @@ class TestSet(SeqMixing, CommonMixin, TestCase):
 
 
 @composite
-def seqof_values_strat(draw, schema=None, do_expl=False):
+def seqof_values_strategy(draw, schema=None, do_expl=False):
     if schema is None:
         schema = draw(sampled_from((Boolean(), Integer())))
     bound_min, bound_max = sorted(draw(sets(
@@ -4270,11 +4320,13 @@ class SeqOfMixing(object):
         obj1 = SeqOf([Boolean(value1)])
         obj2 = SeqOf([Boolean(value2)])
         self.assertEqual(obj1 == obj2, value1 == value2)
+        self.assertEqual(obj1 != obj2, value1 != value2)
         self.assertEqual(obj1 == list(obj2), value1 == value2)
         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
         obj1 = SeqOf([Boolean(value1)], impl=tag1)
         obj2 = SeqOf([Boolean(value1)], impl=tag2)
         self.assertEqual(obj1 == obj2, tag1 == tag2)
+        self.assertEqual(obj1 != obj2, tag1 != tag2)
 
     @given(lists(booleans()))
     def test_iter(self, values):
@@ -4376,7 +4428,7 @@ class SeqOfMixing(object):
             default_initial,
             optional_initial,
             _decoded_initial,
-        ) = d.draw(seqof_values_strat())
+        ) = d.draw(seqof_values_strategy())
 
         class SeqOf(self.base_klass):
             schema = schema_initial
@@ -4398,7 +4450,7 @@ class SeqOfMixing(object):
             default,
             optional,
             _decoded,
-        ) = d.draw(seqof_values_strat(
+        ) = d.draw(seqof_values_strategy(
             schema=schema_initial,
             do_expl=impl_initial is None,
         ))
@@ -4451,7 +4503,7 @@ class SeqOfMixing(object):
             bounds or bounds_initial or (0, float("+inf")),
         )
 
-    @given(seqof_values_strat())
+    @given(seqof_values_strategy())
     def test_copy(self, values):
         _schema, value, bounds, impl, expl, default, optional, _decoded = values
 
@@ -4536,7 +4588,7 @@ class SeqOfMixing(object):
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
-        seqof_values_strat(schema=Integer()),
+        seqof_values_strategy(schema=Integer()),
         lists(integers().map(Integer)),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
@@ -4833,3 +4885,253 @@ class TestAutoAddSlots(TestCase):
         with self.assertRaises(AttributeError):
             inher = Inher()
             inher.unexistent = "whatever"
+
+
+class TestOIDDefines(TestCase):
+    @given(data_strategy())
+    def runTest(self, d):
+        value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
+        value_name_chosen = d.draw(sampled_from(value_names))
+        oids = [
+            ObjectIdentifier(oid)
+            for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
+        ]
+        oid_chosen = d.draw(sampled_from(oids))
+        values = d.draw(lists(
+            integers(),
+            min_size=len(value_names),
+            max_size=len(value_names),
+        ))
+        _schema = [
+            ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
+                oid: Integer() for oid in oids[:-1]
+            }),))),
+        ]
+        for i, value_name in enumerate(value_names):
+            _schema.append((value_name, Any(expl=tag_ctxp(i))))
+
+        class Seq(Sequence):
+            schema = _schema
+        seq = Seq()
+        for value_name, value in zip(value_names, values):
+            seq[value_name] = Any(Integer(value).encode())
+        seq["type"] = oid_chosen
+        seq, _ = Seq().decode(seq.encode())
+        for value_name in value_names:
+            if value_name == value_name_chosen:
+                continue
+            self.assertIsNone(seq[value_name].defined)
+        if value_name_chosen in oids[:-1]:
+            self.assertIsNotNone(seq[value_name_chosen].defined)
+            self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
+            self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
+
+
+class TestDefinesByPath(TestCase):
+    def test_generated(self):
+        class Seq(Sequence):
+            schema = (
+                ("type", ObjectIdentifier()),
+                ("value", OctetString(expl=tag_ctxc(123))),
+            )
+
+        class SeqInner(Sequence):
+            schema = (
+                ("typeInner", ObjectIdentifier()),
+                ("valueInner", Any()),
+            )
+
+        class PairValue(SetOf):
+            schema = Any()
+
+        class Pair(Sequence):
+            schema = (
+                ("type", ObjectIdentifier()),
+                ("value", PairValue()),
+            )
+
+        class Pairs(SequenceOf):
+            schema = Pair()
+
+        (
+            type_integered,
+            type_sequenced,
+            type_innered,
+            type_octet_stringed,
+        ) = [
+            ObjectIdentifier(oid)
+            for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
+        ]
+        seq_integered = Seq()
+        seq_integered["type"] = type_integered
+        seq_integered["value"] = OctetString(Integer(123).encode())
+        seq_integered_raw = seq_integered.encode()
+
+        pairs = Pairs()
+        pairs_input = (
+            (type_octet_stringed, OctetString(b"whatever")),
+            (type_integered, Integer(123)),
+            (type_octet_stringed, OctetString(b"whenever")),
+            (type_integered, Integer(234)),
+        )
+        for t, v in pairs_input:
+            pair = Pair()
+            pair["type"] = t
+            pair["value"] = PairValue((Any(v),))
+            pairs.append(pair)
+        seq_inner = SeqInner()
+        seq_inner["typeInner"] = type_innered
+        seq_inner["valueInner"] = Any(pairs)
+        seq_sequenced = Seq()
+        seq_sequenced["type"] = type_sequenced
+        seq_sequenced["value"] = OctetString(seq_inner.encode())
+        seq_sequenced_raw = seq_sequenced.encode()
+
+        defines_by_path = []
+        seq_integered, _ = Seq().decode(seq_integered_raw)
+        self.assertIsNone(seq_integered["value"].defined)
+        defines_by_path.append(
+            (("type",), ((("value",), {
+                type_integered: Integer(),
+                type_sequenced: SeqInner(),
+            }),))
+        )
+        seq_integered, _ = Seq().decode(
+            seq_integered_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
+        self.assertIsNotNone(seq_integered["value"].defined)
+        self.assertEqual(seq_integered["value"].defined[0], type_integered)
+        self.assertEqual(seq_integered["value"].defined[1], Integer(123))
+
+        seq_sequenced, _ = Seq().decode(
+            seq_sequenced_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
+        self.assertIsNotNone(seq_sequenced["value"].defined)
+        self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
+        seq_inner = seq_sequenced["value"].defined[1]
+        self.assertIsNone(seq_inner["valueInner"].defined)
+
+        defines_by_path.append((
+            ("value", decode_path_defby(type_sequenced), "typeInner"),
+            ((("valueInner",), {type_innered: Pairs()}),),
+        ))
+        seq_sequenced, _ = Seq().decode(
+            seq_sequenced_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
+        self.assertIsNotNone(seq_sequenced["value"].defined)
+        self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
+        seq_inner = seq_sequenced["value"].defined[1]
+        self.assertIsNotNone(seq_inner["valueInner"].defined)
+        self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
+        pairs = seq_inner["valueInner"].defined[1]
+        for pair in pairs:
+            self.assertIsNone(pair["value"][0].defined)
+
+        defines_by_path.append((
+            (
+                "value",
+                decode_path_defby(type_sequenced),
+                "valueInner",
+                decode_path_defby(type_innered),
+                any,
+                "type",
+            ),
+            ((("value",), {
+                type_integered: Integer(),
+                type_octet_stringed: OctetString(),
+            }),),
+        ))
+        seq_sequenced, _ = Seq().decode(
+            seq_sequenced_raw,
+            ctx={"defines_by_path": defines_by_path},
+        )
+        self.assertIsNotNone(seq_sequenced["value"].defined)
+        self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
+        seq_inner = seq_sequenced["value"].defined[1]
+        self.assertIsNotNone(seq_inner["valueInner"].defined)
+        self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
+        pairs_got = seq_inner["valueInner"].defined[1]
+        for pair_input, pair_got in zip(pairs_input, pairs_got):
+            self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
+            self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
+
+    @given(oid_strategy(), integers())
+    def test_simple(self, oid, tgt):
+        class Inner(Sequence):
+            schema = (
+                ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
+                    ObjectIdentifier(oid): Integer(),
+                }),))),
+            )
+
+        class Outer(Sequence):
+            schema = (
+                ("inner", Inner()),
+                ("tgt", OctetString()),
+            )
+
+        inner = Inner()
+        inner["oid"] = ObjectIdentifier(oid)
+        outer = Outer()
+        outer["inner"] = inner
+        outer["tgt"] = OctetString(Integer(tgt).encode())
+        decoded, _ = Outer().decode(outer.encode())
+        self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
+
+class TestAbsDecodePath(TestCase):
+    @given(
+        lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
+    )
+    def test_concat(self, decode_path, rel_path):
+        self.assertSequenceEqual(
+            abs_decode_path(decode_path, rel_path),
+            decode_path + rel_path,
+        )
+
+    @given(
+        lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
+    )
+    def test_abs(self, decode_path, rel_path):
+        self.assertSequenceEqual(
+            abs_decode_path(decode_path, ("/",) + rel_path),
+            rel_path,
+        )
+
+    @given(
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
+        integers(min_value=1, max_value=3),
+        lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
+    )
+    def test_dots(self, decode_path, number_of_dots, rel_path):
+        self.assertSequenceEqual(
+            abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
+            decode_path[:-number_of_dots] + rel_path,
+        )
+
+
+class TestStrictDefaultExistence(TestCase):
+    @given(data_strategy())
+    def runTest(self, d):
+        count = d.draw(integers(min_value=1, max_value=10))
+        chosen = d.draw(integers(min_value=0, max_value=count - 1))
+        _schema = [
+            ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
+            for i in range(count)
+        ]
+
+        class Seq(Sequence):
+            schema = _schema
+        seq = Seq()
+        for i in range(count):
+            seq["int%d" % i] = Integer(123)
+        raw = seq.encode()
+        chosen = "int%d" % chosen
+        seq.specs[chosen] = seq.specs[chosen](default=123)
+        seq.decode(raw)
+        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            seq.decode(raw, ctx={"strict_default_existence": True})