]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - tests/test_pyderasn.py
Raise copyright years
[pyderasn.git] / tests / test_pyderasn.py
index a39d125ee036d8c12259732254a11543aec88c15..e8f8bdf4570e1c52adbf2b7a7c5a827c47a2099c 100644 (file)
@@ -1,6 +1,6 @@
 # coding: utf-8
 # coding: utf-8
-# PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
-# Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
+# PyDERASN -- Python ASN.1 DER/CER/BER codec with abstract structures
+# Copyright (C) 2017-2024 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
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU Lesser General Public License as
 from copy import copy
 from copy import deepcopy
 from datetime import datetime
 from copy import copy
 from copy import deepcopy
 from datetime import datetime
+from datetime import timedelta
 from importlib import import_module
 from importlib import import_module
+from io import BytesIO
+from operator import attrgetter
+from os import environ
+from os import urandom
+from random import random
 from string import ascii_letters
 from string import digits
 from string import printable
 from string import whitespace
 from string import ascii_letters
 from string import digits
 from string import printable
 from string import whitespace
+from time import mktime
 from time import time
 from unittest import TestCase
 from time import time
 from unittest import TestCase
+from unittest.mock import patch
 
 
+from dateutil.tz import UTC
 from hypothesis import assume
 from hypothesis import given
 from hypothesis import settings
 from hypothesis import assume
 from hypothesis import given
 from hypothesis import settings
@@ -45,18 +54,9 @@ from hypothesis.strategies import sampled_from
 from hypothesis.strategies import sets
 from hypothesis.strategies import text
 from hypothesis.strategies import tuples
 from hypothesis.strategies import sets
 from hypothesis.strategies import text
 from hypothesis.strategies import tuples
-from six import assertRaisesRegex
-from six import binary_type
-from six import byte2int
-from six import indexbytes
-from six import int2byte
-from six import iterbytes
-from six import PY2
-from six import text_type
-from six import unichr as six_unichr
-from six.moves.cPickle import dumps as pickle_dumps
-from six.moves.cPickle import HIGHEST_PROTOCOL as pickle_proto
-from six.moves.cPickle import loads as pickle_loads
+from pickle import dumps as pickle_dumps
+from pickle import HIGHEST_PROTOCOL as pickle_proto
+from pickle import loads as pickle_loads
 
 from pyderasn import _pp
 from pyderasn import abs_decode_path
 
 from pyderasn import _pp
 from pyderasn import abs_decode_path
@@ -68,6 +68,8 @@ from pyderasn import BoundsError
 from pyderasn import Choice
 from pyderasn import DecodeError
 from pyderasn import DecodePathDefBy
 from pyderasn import Choice
 from pyderasn import DecodeError
 from pyderasn import DecodePathDefBy
+from pyderasn import encode2pass
+from pyderasn import encode_cer
 from pyderasn import Enumerated
 from pyderasn import EOC
 from pyderasn import EOC_LEN
 from pyderasn import Enumerated
 from pyderasn import EOC
 from pyderasn import EOC_LEN
@@ -121,10 +123,13 @@ from pyderasn import UTCTime
 from pyderasn import UTF8String
 from pyderasn import VideotexString
 from pyderasn import VisibleString
 from pyderasn import UTF8String
 from pyderasn import VideotexString
 from pyderasn import VisibleString
+import pyderasn
 
 
 
 
+max_examples = environ.get("MAX_EXAMPLES")
 settings.register_profile("local", settings(
     deadline=5000,
 settings.register_profile("local", settings(
     deadline=5000,
+    **({"max_examples": int(max_examples)} if max_examples else {})
 ))
 settings.load_profile("local")
 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
 ))
 settings.load_profile("local")
 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
@@ -155,9 +160,11 @@ def register_class(klass):
 
 
 def assert_exceeding_data(self, call, junk):
 
 
 def assert_exceeding_data(self, call, junk):
-    if len(junk) > 0:
-        with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)):
-            call()
+    if len(junk) <= 0:
+        return
+    with self.assertRaisesRegex(ExceedingData, "%d trailing bytes" % len(junk)) as err:
+        call()
+    repr(err)
 
 
 class TestHex(TestCase):
 
 
 class TestHex(TestCase):
@@ -179,8 +186,8 @@ class TestTagCoder(TestCase):
         self.assertEqual(tag_decode(raw), (klass, form, num))
         self.assertEqual(len(raw), 1)
         self.assertEqual(
         self.assertEqual(tag_decode(raw), (klass, form, num))
         self.assertEqual(len(raw), 1)
         self.assertEqual(
-            byte2int(tag_encode(klass=klass, form=form, num=0)),
-            byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
+            tag_encode(klass=klass, form=form, num=0)[0],
+            raw[0] & (1 << 7 | 1 << 6 | 1 << 5),
         )
         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
         self.assertSequenceEqual(stripped.tobytes(), raw)
         )
         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
         self.assertSequenceEqual(stripped.tobytes(), raw)
@@ -199,11 +206,11 @@ class TestTagCoder(TestCase):
         self.assertEqual(tag_decode(raw), (klass, form, num))
         self.assertGreater(len(raw), 1)
         self.assertEqual(
         self.assertEqual(tag_decode(raw), (klass, form, num))
         self.assertGreater(len(raw), 1)
         self.assertEqual(
-            byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
-            byte2int(raw[:1]),
+            tag_encode(klass=klass, form=form, num=0)[0] | 31,
+            raw[0],
         )
         )
-        self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
-        self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
+        self.assertEqual(raw[-1] & 0x80, 0)
+        self.assertTrue(all(b & 0x80 > 0 for b in raw[1:-1]))
         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
         self.assertSequenceEqual(stripped.tobytes(), raw)
         self.assertEqual(tlen, len(raw))
         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
         self.assertSequenceEqual(stripped.tobytes(), raw)
         self.assertEqual(tlen, len(raw))
@@ -215,7 +222,7 @@ class TestTagCoder(TestCase):
         raw = bytearray(tag_encode(num=num))
         for i in range(1, len(raw)):
             raw[i] |= 0x80
         raw = bytearray(tag_encode(num=num))
         for i in range(1, len(raw)):
             raw[i] |= 0x80
-        with assertRaisesRegex(self, DecodeError, "unfinished tag"):
+        with self.assertRaisesRegex(DecodeError, "unfinished tag"):
             tag_strip(bytes(raw))
 
     def test_go_vectors_valid(self):
             tag_strip(bytes(raw))
 
     def test_go_vectors_valid(self):
@@ -257,11 +264,24 @@ class TestTagCoder(TestCase):
         integers(min_value=0, max_value=2),
     )
     def test_long_instead_of_short(self, l, dummy_num):
         integers(min_value=0, max_value=2),
     )
     def test_long_instead_of_short(self, l, dummy_num):
-        octets = (b"\x00" * dummy_num) + int2byte(l)
-        octets = int2byte((dummy_num + 1) | 0x80) + octets
+        octets = (b"\x00" * dummy_num) + bytes([l])
+        octets = bytes([(dummy_num + 1) | 0x80]) + octets
         with self.assertRaises(DecodeError):
             len_decode(octets)
 
         with self.assertRaises(DecodeError):
             len_decode(octets)
 
+    @given(tag_classes, tag_forms, integers(min_value=31))
+    def test_leading_zero_byte(self, klass, form, num):
+        raw = tag_encode(klass=klass, form=form, num=num)
+        raw = b"".join((raw[:1], b"\x80", raw[1:]))
+        with self.assertRaisesRegex(DecodeError, "leading zero byte"):
+            tag_strip(raw)
+
+    @given(tag_classes, tag_forms, integers(max_value=30, min_value=0))
+    def test_unexpected_long_form(self, klass, form, num):
+        raw = bytes([klass | form | 31, num])
+        with self.assertRaisesRegex(DecodeError, "unexpected long form"):
+            tag_strip(raw)
+
 
 class TestLenCoder(TestCase):
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
 
 class TestLenCoder(TestCase):
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
@@ -286,9 +306,9 @@ class TestLenCoder(TestCase):
         raw = len_encode(l) + junk
         decoded, llen, tail = len_decode(memoryview(raw))
         self.assertEqual(decoded, l)
         raw = len_encode(l) + junk
         decoded, llen, tail = len_decode(memoryview(raw))
         self.assertEqual(decoded, l)
-        self.assertEqual((llen - 1) | 0x80, byte2int(raw))
+        self.assertEqual((llen - 1) | 0x80, raw[0])
         self.assertEqual(llen, len(raw) - len(junk))
         self.assertEqual(llen, len(raw) - len(junk))
-        self.assertNotEqual(indexbytes(raw, 1), 0)
+        self.assertNotEqual(raw[1], 0)
         self.assertSequenceEqual(tail.tobytes(), junk)
 
     def test_empty(self):
         self.assertSequenceEqual(tail.tobytes(), junk)
 
     def test_empty(self):
@@ -307,8 +327,6 @@ text_printable = text(alphabet=printable, min_size=1)
 @composite
 def text_letters(draw):
     result = draw(text(alphabet=ascii_letters, min_size=1))
 @composite
 def text_letters(draw):
     result = draw(text(alphabet=ascii_letters, min_size=1))
-    if PY2:
-        result = result.encode("ascii")
     return result
 
 
     return result
 
 
@@ -337,14 +355,20 @@ class CommonMixin(object):
         obj = Inherited()
         self.assertSequenceEqual(obj.impl, impl_tag)
         self.assertFalse(obj.expled)
         obj = Inherited()
         self.assertSequenceEqual(obj.impl, impl_tag)
         self.assertFalse(obj.expled)
+        if obj.ready:
+            tag_class, _, tag_num = tag_decode(impl_tag)
+            self.assertEqual(obj.tag_order, (tag_class, tag_num))
 
 
-    @given(binary())
+    @given(binary(min_size=1))
     def test_expl_inherited(self, expl_tag):
         class Inherited(self.base_klass):
             expl = expl_tag
         obj = Inherited()
         self.assertSequenceEqual(obj.expl, expl_tag)
         self.assertTrue(obj.expled)
     def test_expl_inherited(self, expl_tag):
         class Inherited(self.base_klass):
             expl = expl_tag
         obj = Inherited()
         self.assertSequenceEqual(obj.expl, expl_tag)
         self.assertTrue(obj.expled)
+        if obj.ready:
+            tag_class, _, tag_num = tag_decode(expl_tag)
+            self.assertEqual(obj.tag_order, (tag_class, tag_num))
 
     def assert_copied_basic_fields(self, obj, obj_copied):
         self.assertEqual(obj, obj_copied)
 
     def assert_copied_basic_fields(self, obj, obj_copied):
         self.assertEqual(obj, obj_copied)
@@ -355,6 +379,8 @@ class CommonMixin(object):
         self.assertEqual(obj.offset, obj_copied.offset)
         self.assertEqual(obj.llen, obj_copied.llen)
         self.assertEqual(obj.vlen, obj_copied.vlen)
         self.assertEqual(obj.offset, obj_copied.offset)
         self.assertEqual(obj.llen, obj_copied.llen)
         self.assertEqual(obj.vlen, obj_copied.vlen)
+        if obj.ready:
+            self.assertEqual(obj.tag_order, obj_copied.tag_order)
 
 
 @composite
 
 
 @composite
@@ -402,6 +428,8 @@ class TestBoolean(CommonMixin, TestCase):
         pprint(obj, big_blobs=True, with_decode_path=True)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         pprint(obj, big_blobs=True, with_decode_path=True)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         repr(err.exception)
         obj = Boolean(value)
         self.assertTrue(obj.ready)
         repr(err.exception)
         obj = Boolean(value)
         self.assertTrue(obj.ready)
@@ -409,7 +437,7 @@ class TestBoolean(CommonMixin, TestCase):
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
 
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
 
-    @given(booleans(), booleans(), binary(), binary())
+    @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
     def test_comparison(self, value1, value2, tag1, tag2):
         for klass in (Boolean, BooleanInherited):
             obj1 = klass(value1)
     def test_comparison(self, value1, value2, tag1, tag2):
         for klass in (Boolean, BooleanInherited):
             obj1 = klass(value1)
@@ -486,6 +514,8 @@ class TestBoolean(CommonMixin, TestCase):
         obj = Boolean(value, impl=tag_impl)
         with self.assertRaises(NotEnoughData):
             obj.decode(obj.encode()[:-1])
         obj = Boolean(value, impl=tag_impl)
         with self.assertRaises(NotEnoughData):
             obj.decode(obj.encode()[:-1])
+        with self.assertRaises(NotEnoughData):
+            obj.decode(encode2pass(obj)[:-1])
 
     @given(
         booleans(),
 
     @given(
         booleans(),
@@ -495,6 +525,8 @@ class TestBoolean(CommonMixin, TestCase):
         obj = Boolean(value, expl=tag_expl)
         with self.assertRaises(NotEnoughData):
             obj.decode(obj.encode()[:-1])
         obj = Boolean(value, expl=tag_expl)
         with self.assertRaises(NotEnoughData):
             obj.decode(obj.encode()[:-1])
+        with self.assertRaises(NotEnoughData):
+            obj.decode(encode2pass(obj)[:-1])
 
     @given(
         integers(min_value=31),
 
     @given(
         integers(min_value=31),
@@ -567,8 +599,9 @@ class TestBoolean(CommonMixin, TestCase):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
         for klass in (Boolean, BooleanInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
         for klass in (Boolean, BooleanInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -582,15 +615,23 @@ class TestBoolean(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
+            self.assertSequenceEqual(encode_cer(obj), obj_encoded)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
-            obj_expled_encoded = obj_expled.encode()
+            obj_expled_cer = encode_cer(obj_expled)
+            self.assertNotEqual(obj_expled_cer, obj_encoded)
+            self.assertSequenceEqual(
+                obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+                obj_expled.encode(),
+            )
+            obj_expled_hex_encoded = obj_expled.hexencode()
             ctx_copied = deepcopy(ctx_dummy)
             ctx_copied = deepcopy(ctx_dummy)
-            obj_decoded, tail = obj_expled.decode(
-                obj_expled_encoded + tail_junk,
+            obj_decoded, tail = obj_expled.hexdecode(
+                obj_expled_hex_encoded + hexenc(tail_junk),
                 offset=offset,
                 ctx=ctx_copied,
             )
                 offset=offset,
                 ctx=ctx_copied,
             )
@@ -603,7 +644,7 @@ class TestBoolean(CommonMixin, TestCase):
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bool(obj_decoded), bool(obj_expled))
             self.assertEqual(bool(obj_decoded), bool(obj))
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bool(obj_decoded), bool(obj_expled))
             self.assertEqual(bool(obj_decoded), bool(obj))
-            self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
+            self.assertSequenceEqual(obj_decoded.hexencode(), obj_expled_hex_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
             self.assertEqual(
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
             self.assertEqual(
@@ -619,11 +660,26 @@ class TestBoolean(CommonMixin, TestCase):
             self.assertEqual(obj_decoded.expl_offset, offset)
             assert_exceeding_data(
                 self,
             self.assertEqual(obj_decoded.expl_offset, offset)
             assert_exceeding_data(
                 self,
-                lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
+                lambda: obj_expled.hexdecod(obj_expled_hex_encoded + hexenc(tail_junk)),
                 tail_junk,
             )
 
                 tail_junk,
             )
 
-    @given(integers(min_value=2))
+            evgens = list(obj_expled.decode_evgen(
+                hexdec(obj_expled_hex_encoded) + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj, obj_decoded)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
+    @given(integers(min_value=2, max_value=10))
     def test_invalid_len(self, l):
         with self.assertRaises(InvalidLength):
             Boolean().decode(b"".join((
     def test_invalid_len(self, l):
         with self.assertRaises(InvalidLength):
             Boolean().decode(b"".join((
@@ -634,20 +690,15 @@ class TestBoolean(CommonMixin, TestCase):
 
     @given(integers(min_value=0 + 1, max_value=255 - 1))
     def test_ber_value(self, value):
 
     @given(integers(min_value=0 + 1, max_value=255 - 1))
     def test_ber_value(self, value):
-        with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
+        with self.assertRaisesRegex(DecodeError, "unacceptable Boolean value"):
             Boolean().decode(b"".join((
                 Boolean.tag_default,
                 len_encode(1),
             Boolean().decode(b"".join((
                 Boolean.tag_default,
                 len_encode(1),
-                int2byte(value),
+                bytes([value]),
             )))
             )))
-        obj, _ = Boolean().decode(
-            b"".join((
-                Boolean.tag_default,
-                len_encode(1),
-                int2byte(value),
-            )),
-            ctx={"bered": True},
-        )
+        encoded = b"".join((Boolean.tag_default, len_encode(1), bytes([value])))
+        obj, _ = Boolean().decode(encoded, ctx={"bered": True})
+        list(Boolean().decode_evgen(encoded, ctx={"bered": True}))
         self.assertTrue(bool(obj))
         self.assertTrue(obj.ber_encoded)
         self.assertFalse(obj.lenindef)
         self.assertTrue(bool(obj))
         self.assertTrue(obj.ber_encoded)
         self.assertFalse(obj.lenindef)
@@ -665,7 +716,7 @@ class TestBoolean(CommonMixin, TestCase):
         encoded = expl + LENINDEF + Boolean(False).encode()
         with self.assertRaises(LenIndefForm):
             Boolean(expl=expl).decode(encoded + junk)
         encoded = expl + LENINDEF + Boolean(False).encode()
         with self.assertRaises(LenIndefForm):
             Boolean(expl=expl).decode(encoded + junk)
-        with assertRaisesRegex(self, DecodeError, "no EOC"):
+        with self.assertRaisesRegex(DecodeError, "no EOC"):
             Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
         obj, tail = Boolean(expl=expl).decode(
             encoded + EOC + junk,
             Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
         obj, tail = Boolean(expl=expl).decode(
             encoded + EOC + junk,
@@ -709,6 +760,7 @@ class TestBoolean(CommonMixin, TestCase):
         with self.assertRaises(LenIndefForm):
             SeqOf().decode(encoded)
         seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
         with self.assertRaises(LenIndefForm):
             SeqOf().decode(encoded)
         seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
+        list(SeqOf().decode_evgen(encoded, ctx={"bered": True}))
         self.assertSequenceEqual(tail, b"")
         self.assertSequenceEqual([bool(v) for v in seqof], values)
         self.assertSetEqual(
         self.assertSequenceEqual(tail, b"")
         self.assertSequenceEqual([bool(v) for v in seqof], values)
         self.assertSetEqual(
@@ -820,6 +872,8 @@ class TestInteger(CommonMixin, TestCase):
         pprint(obj, big_blobs=True, with_decode_path=True)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         pprint(obj, big_blobs=True, with_decode_path=True)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         repr(err.exception)
         obj = Integer(value)
         self.assertTrue(obj.ready)
         repr(err.exception)
         obj = Integer(value)
         self.assertTrue(obj.ready)
@@ -828,7 +882,7 @@ class TestInteger(CommonMixin, TestCase):
         pprint(obj, big_blobs=True, with_decode_path=True)
         hash(obj)
 
         pprint(obj, big_blobs=True, with_decode_path=True)
         hash(obj)
 
-    @given(integers(), integers(), binary(), binary())
+    @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
     def test_comparison(self, value1, value2, tag1, tag2):
         for klass in (Integer, IntegerInherited):
             obj1 = klass(value1)
     def test_comparison(self, value1, value2, tag1, tag2):
         for klass in (Integer, IntegerInherited):
             obj1 = klass(value1)
@@ -877,19 +931,27 @@ class TestInteger(CommonMixin, TestCase):
         with self.assertRaises(BoundsError) as err:
             Integer(value=values[0], bounds=(values[1], values[2]))
         repr(err.exception)
         with self.assertRaises(BoundsError) as err:
             Integer(value=values[0], bounds=(values[1], values[2]))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             Integer(bounds=(values[1], values[2])).decode(
                 Integer(values[0]).encode()
             )
         repr(err.exception)
             Integer(bounds=(values[1], values[2])).decode(
                 Integer(values[0]).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            Integer(bounds=(values[1], values[2])).decode(
+                encode2pass(Integer(values[0]))
+            )
         with self.assertRaises(BoundsError) as err:
             Integer(value=values[2], bounds=(values[0], values[1]))
         repr(err.exception)
         with self.assertRaises(BoundsError) as err:
             Integer(value=values[2], bounds=(values[0], values[1]))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             Integer(bounds=(values[0], values[1])).decode(
                 Integer(values[2]).encode()
             )
         repr(err.exception)
             Integer(bounds=(values[0], values[1])).decode(
                 Integer(values[2]).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            Integer(bounds=(values[0], values[1])).decode(
+                encode2pass(Integer(values[2]))
+            )
 
     @given(data_strategy())
     def test_call(self, d):
 
     @given(data_strategy())
     def test_call(self, d):
@@ -1065,8 +1127,9 @@ class TestInteger(CommonMixin, TestCase):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
         for klass in (Integer, IntegerInherited):
             _, _, _, _, default, optional, _, _decoded = values
             obj = klass(
         for klass in (Integer, IntegerInherited):
             _, _, _, _, default, optional, _, _decoded = values
             obj = klass(
@@ -1080,12 +1143,20 @@ class TestInteger(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
+            self.assertSequenceEqual(encode_cer(obj), obj_encoded)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            obj_expled_cer = encode_cer(obj_expled)
+            self.assertNotEqual(obj_expled_cer, obj_encoded)
+            self.assertSequenceEqual(
+                obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+                obj_expled_encoded,
+            )
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -1121,6 +1192,21 @@ class TestInteger(CommonMixin, TestCase):
                 tail_junk,
             )
 
                 tail_junk,
             )
 
+            evgens = list(obj_expled.decode_evgen(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj, obj_decoded)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
     def test_go_vectors_valid(self):
         for data, expect in ((
                 (b"\x00", 0),
     def test_go_vectors_valid(self):
         for data, expect in ((
                 (b"\x00", 0),
@@ -1186,6 +1272,8 @@ def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=
         if generation_choice == 2 or draw(booleans()):
             return draw(binary(max_size=len(schema) // 8))
         if generation_choice == 3 or draw(booleans()):
         if generation_choice == 2 or draw(booleans()):
             return draw(binary(max_size=len(schema) // 8))
         if generation_choice == 3 or draw(booleans()):
+            if len(schema) == 0:
+                return ()
             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
         return None
     value = _value(value_required)
             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
         return None
     value = _value(value_required)
@@ -1296,6 +1384,8 @@ class TestBitString(CommonMixin, TestCase):
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         obj = BitString(value)
         self.assertTrue(obj.ready)
         repr(obj)
         obj = BitString(value)
         self.assertTrue(obj.ready)
         repr(obj)
@@ -1459,6 +1549,7 @@ class TestBitString(CommonMixin, TestCase):
         tail_junk = d.draw(binary(max_size=5))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         tail_junk = d.draw(binary(max_size=5))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
+        decode_path = d.draw(decode_path_strat)
         for klass in (BitString, BitStringInherited):
             class BS(klass):
                 schema = _schema
         for klass in (BitString, BitStringInherited):
             class BS(klass):
                 schema = _schema
@@ -1473,12 +1564,20 @@ class TestBitString(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
+            self.assertSequenceEqual(encode_cer(obj), obj_encoded)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            obj_expled_cer = encode_cer(obj_expled)
+            self.assertNotEqual(obj_expled_cer, obj_encoded)
+            self.assertSequenceEqual(
+                obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+                obj_expled_encoded,
+            )
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -1518,13 +1617,27 @@ class TestBitString(CommonMixin, TestCase):
                 tail_junk,
             )
 
                 tail_junk,
             )
 
+            evgens = list(obj_expled.decode_evgen(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
     @given(integers(min_value=1, max_value=255))
     def test_bad_zero_value(self, pad_size):
         with self.assertRaises(DecodeError):
             BitString().decode(b"".join((
                 BitString.tag_default,
                 len_encode(1),
     @given(integers(min_value=1, max_value=255))
     def test_bad_zero_value(self, pad_size):
         with self.assertRaises(DecodeError):
             BitString().decode(b"".join((
                 BitString.tag_default,
                 len_encode(1),
-                int2byte(pad_size),
+                bytes([pad_size]),
             )))
 
     def test_go_vectors_invalid(self):
             )))
 
     def test_go_vectors_invalid(self):
@@ -1564,6 +1677,7 @@ class TestBitString(CommonMixin, TestCase):
         self.assertTrue(obj[9])
         self.assertFalse(obj[17])
 
         self.assertTrue(obj[9])
         self.assertFalse(obj[17])
 
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
         integers(min_value=1, max_value=30),
         lists(
     @given(
         integers(min_value=1, max_value=30),
         lists(
@@ -1580,8 +1694,9 @@ class TestBitString(CommonMixin, TestCase):
         ),
         lists(booleans(), min_size=1),
         binary(),
         ),
         lists(booleans(), min_size=1),
         binary(),
+        decode_path_strat,
     )
     )
-    def test_constructed(self, impl, chunk_inputs, chunk_last_bits, junk):
+    def test_constructed(self, impl, chunk_inputs, chunk_last_bits, junk, decode_path):
         def chunk_constructed(contents):
             return (
                 tag_encode(form=TagFormConstructed, num=3) +
         def chunk_constructed(contents):
             return (
                 tag_encode(form=TagFormConstructed, num=3) +
@@ -1590,21 +1705,27 @@ class TestBitString(CommonMixin, TestCase):
                 EOC
             )
         chunks = []
                 EOC
             )
         chunks = []
+        chunks_len_expected = []
         payload_expected = b""
         bit_len_expected = 0
         for chunk_input in chunk_inputs:
         payload_expected = b""
         bit_len_expected = 0
         for chunk_input in chunk_inputs:
-            if isinstance(chunk_input, binary_type):
+            if isinstance(chunk_input, bytes):
                 chunks.append(BitString(chunk_input).encode())
                 payload_expected += chunk_input
                 bit_len_expected += len(chunk_input) * 8
                 chunks.append(BitString(chunk_input).encode())
                 payload_expected += chunk_input
                 bit_len_expected += len(chunk_input) * 8
+                chunks_len_expected.append(len(chunk_input) + 1)
             else:
                 chunks.append(chunk_constructed(chunk_input))
                 payload = b"".join(chunk_input)
                 payload_expected += payload
                 bit_len_expected += len(payload) * 8
             else:
                 chunks.append(chunk_constructed(chunk_input))
                 payload = b"".join(chunk_input)
                 payload_expected += payload
                 bit_len_expected += len(payload) * 8
+                for c in chunk_input:
+                    chunks_len_expected.append(len(c) + 1)
+                chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
         chunk_last = BitString("'%s'B" % "".join(
             "1" if bit else "0" for bit in chunk_last_bits
         ))
         chunk_last = BitString("'%s'B" % "".join(
             "1" if bit else "0" for bit in chunk_last_bits
         ))
+        chunks_len_expected.append(BitString().decod(chunk_last.encode()).vlen)
         payload_expected += bytes(chunk_last)
         bit_len_expected += chunk_last.bit_len
         encoded_indefinite = (
         payload_expected += bytes(chunk_last)
         bit_len_expected += chunk_last.bit_len
         encoded_indefinite = (
@@ -1620,7 +1741,7 @@ class TestBitString(CommonMixin, TestCase):
             b"".join(chunks) +
             chunk_last.encode()
         )
             b"".join(chunks) +
             chunk_last.encode()
         )
-        with assertRaisesRegex(self, DecodeError, "unallowed BER"):
+        with self.assertRaisesRegex(DecodeError, "unallowed BER"):
             BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
         for lenindef_expected, encoded in (
                 (True, encoded_indefinite),
             BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
         for lenindef_expected, encoded in (
                 (True, encoded_indefinite),
@@ -1641,13 +1762,26 @@ class TestBitString(CommonMixin, TestCase):
             self.assertEqual(obj.lenindef, lenindef_expected)
             self.assertTrue(obj.bered)
             self.assertEqual(len(encoded), obj.tlvlen)
             self.assertEqual(obj.lenindef, lenindef_expected)
             self.assertTrue(obj.bered)
             self.assertEqual(len(encoded), obj.tlvlen)
+            repr(obj)
+            list(obj.pps())
+            pprint(obj, big_blobs=True, with_decode_path=True)
+
+            evgens = list(BitString(impl=tag_encode(impl)).decode_evgen(
+                encoded,
+                decode_path=decode_path,
+                ctx={"bered": True},
+            ))
+            self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
+            for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
+                self.assertGreater(len(dp), len(decode_path))
+                self.assertEqual(obj.vlen, chunk_len_expected)
 
     @given(
         integers(min_value=0),
         decode_path_strat,
     )
     def test_ber_definite_too_short(self, offset, decode_path):
 
     @given(
         integers(min_value=0),
         decode_path_strat,
     )
     def test_ber_definite_too_short(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "longer than data") as err:
+        with self.assertRaisesRegex(DecodeError, "longer than data") as err:
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + len_encode(1),
                 offset=offset,
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + len_encode(1),
                 offset=offset,
@@ -1662,7 +1796,7 @@ class TestBitString(CommonMixin, TestCase):
         decode_path_strat,
     )
     def test_ber_definite_no_data(self, offset, decode_path):
         decode_path_strat,
     )
     def test_ber_definite_no_data(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "zero length") as err:
+        with self.assertRaisesRegex(DecodeError, "zero length") as err:
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + len_encode(0),
                 offset=offset,
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + len_encode(0),
                 offset=offset,
@@ -1697,7 +1831,7 @@ class TestBitString(CommonMixin, TestCase):
     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
         bs = BitString(b"data").encode()
         bs_longer = BitString(b"data-longer").encode()
     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
         bs = BitString(b"data").encode()
         bs_longer = BitString(b"data-longer").encode()
-        with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "chunk out of bounds") as err:
             BitString().decode(
                 (
                     tag_encode(3, form=TagFormConstructed) +
             BitString().decode(
                 (
                     tag_encode(3, form=TagFormConstructed) +
@@ -1717,7 +1851,7 @@ class TestBitString(CommonMixin, TestCase):
         decode_path_strat,
     )
     def test_ber_indefinite_no_chunks(self, offset, decode_path):
         decode_path_strat,
     )
     def test_ber_indefinite_no_chunks(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "no chunks") as err:
+        with self.assertRaisesRegex(DecodeError, "no chunks") as err:
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
                 offset=offset,
             BitString().decode(
                 tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
                 offset=offset,
@@ -1737,7 +1871,7 @@ class TestBitString(CommonMixin, TestCase):
         chunks.append(bs_short)
         offset = d.draw(integers(min_value=0))
         decode_path = d.draw(decode_path_strat)
         chunks.append(bs_short)
         offset = d.draw(integers(min_value=0))
         decode_path = d.draw(decode_path_strat)
-        with assertRaisesRegex(self, DecodeError, "multiple of 8 bits") as err:
+        with self.assertRaisesRegex(DecodeError, "multiple of 8 bits") as err:
             BitString().decode(
                 (
                     tag_encode(3, form=TagFormConstructed) +
             BitString().decode(
                 (
                     tag_encode(3, form=TagFormConstructed) +
@@ -1777,6 +1911,24 @@ class TestBitString(CommonMixin, TestCase):
         self.assertTrue(obj.lenindef)
         self.assertTrue(obj.bered)
 
         self.assertTrue(obj.lenindef)
         self.assertTrue(obj.bered)
 
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+    @given(integers(min_value=1000, max_value=3000))
+    def test_cer(self, data_len):
+        data = urandom(data_len)
+        encoded = encode_cer(BitString(data))
+        ctx = {"bered": True}
+        self.assertSequenceEqual(bytes(BitString().decod(encoded, ctx=ctx)), data)
+        evgens = list(BitString().decode_evgen(encoded, ctx=ctx))
+        evgens_expected = data_len // 999
+        if evgens_expected * 999 != data_len:
+            evgens_expected += 1
+        evgens_expected += 1
+        self.assertEqual(len(evgens), evgens_expected)
+        for (_, obj, _) in evgens[:-2]:
+            self.assertEqual(obj.vlen, 1000)
+        _, obj, _ = evgens[-2]
+        self.assertEqual(obj.vlen, 1 + data_len - len(evgens[:-2]) * 999)
+
 
 @composite
 def octet_string_values_strategy(draw, do_expl=False):
 
 @composite
 def octet_string_values_strategy(draw, do_expl=False):
@@ -1820,7 +1972,7 @@ class TestOctetString(CommonMixin, TestCase):
 
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
 
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
-            OctetString(text_type(123))
+            OctetString(str(123))
         repr(err.exception)
 
     @given(booleans())
         repr(err.exception)
 
     @given(booleans())
@@ -1838,6 +1990,8 @@ class TestOctetString(CommonMixin, TestCase):
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         obj = OctetString(value)
         self.assertTrue(obj.ready)
         repr(obj)
         obj = OctetString(value)
         self.assertTrue(obj.ready)
         repr(obj)
@@ -1879,20 +2033,28 @@ class TestOctetString(CommonMixin, TestCase):
         with self.assertRaises(BoundsError) as err:
             OctetString(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
         with self.assertRaises(BoundsError) as err:
             OctetString(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             OctetString(bounds=(bound_min, bound_max)).decode(
                 OctetString(value).encode()
             )
         repr(err.exception)
             OctetString(bounds=(bound_min, bound_max)).decode(
                 OctetString(value).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            OctetString(bounds=(bound_min, bound_max)).decode(
+                encode2pass(OctetString(value))
+            )
         value = d.draw(binary(min_size=bound_max + 1))
         with self.assertRaises(BoundsError) as err:
             OctetString(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
         value = d.draw(binary(min_size=bound_max + 1))
         with self.assertRaises(BoundsError) as err:
             OctetString(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             OctetString(bounds=(bound_min, bound_max)).decode(
                 OctetString(value).encode()
             )
         repr(err.exception)
             OctetString(bounds=(bound_min, bound_max)).decode(
                 OctetString(value).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            OctetString(bounds=(bound_min, bound_max)).decode(
+                encode2pass(OctetString(value))
+            )
 
     @given(data_strategy())
     def test_call(self, d):
 
     @given(data_strategy())
     def test_call(self, d):
@@ -2053,8 +2215,19 @@ class TestOctetString(CommonMixin, TestCase):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
+        booleans(),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
+    def test_symmetric(
+            self,
+            values,
+            value,
+            tag_expl,
+            offset,
+            tail_junk,
+            decode_path,
+            keep_memoryview,
+    ):
         for klass in (OctetString, OctetStringInherited):
             _, _, _, _, default, optional, _decoded = values
             obj = klass(
         for klass in (OctetString, OctetStringInherited):
             _, _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -2068,12 +2241,21 @@ class TestOctetString(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
+            self.assertSequenceEqual(encode_cer(obj), obj_encoded)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            obj_expled_cer = encode_cer(obj_expled)
+            self.assertNotEqual(obj_expled_cer, obj_encoded)
+            self.assertSequenceEqual(
+                obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+                obj_expled_encoded,
+            )
+            ctx_dummy["keep_memoryview"] = keep_memoryview
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -2089,6 +2271,10 @@ class TestOctetString(CommonMixin, TestCase):
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
             self.assertNotEqual(obj_decoded, obj)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
+            self.assertIsInstance(
+                obj_decoded._value,
+                memoryview if keep_memoryview else bytes,
+            )
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
@@ -2109,6 +2295,21 @@ class TestOctetString(CommonMixin, TestCase):
                 tail_junk,
             )
 
                 tail_junk,
             )
 
+            evgens = list(obj_expled.decode_evgen(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
         integers(min_value=1, max_value=30),
         lists(
     @given(
         integers(min_value=1, max_value=30),
         lists(
@@ -2124,8 +2325,9 @@ class TestOctetString(CommonMixin, TestCase):
             max_size=3,
         ),
         binary(),
             max_size=3,
         ),
         binary(),
+        decode_path_strat,
     )
     )
-    def test_constructed(self, impl, chunk_inputs, junk):
+    def test_constructed(self, impl, chunk_inputs, junk, decode_path):
         def chunk_constructed(contents):
             return (
                 tag_encode(form=TagFormConstructed, num=4) +
         def chunk_constructed(contents):
             return (
                 tag_encode(form=TagFormConstructed, num=4) +
@@ -2134,15 +2336,20 @@ class TestOctetString(CommonMixin, TestCase):
                 EOC
             )
         chunks = []
                 EOC
             )
         chunks = []
+        chunks_len_expected = []
         payload_expected = b""
         for chunk_input in chunk_inputs:
         payload_expected = b""
         for chunk_input in chunk_inputs:
-            if isinstance(chunk_input, binary_type):
+            if isinstance(chunk_input, bytes):
                 chunks.append(OctetString(chunk_input).encode())
                 payload_expected += chunk_input
                 chunks.append(OctetString(chunk_input).encode())
                 payload_expected += chunk_input
+                chunks_len_expected.append(len(chunk_input))
             else:
                 chunks.append(chunk_constructed(chunk_input))
                 payload = b"".join(chunk_input)
                 payload_expected += payload
             else:
                 chunks.append(chunk_constructed(chunk_input))
                 payload = b"".join(chunk_input)
                 payload_expected += payload
+                for c in chunk_input:
+                    chunks_len_expected.append(len(c))
+                chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
         encoded_indefinite = (
             tag_encode(form=TagFormConstructed, num=impl) +
             LENINDEF +
         encoded_indefinite = (
             tag_encode(form=TagFormConstructed, num=impl) +
             LENINDEF +
@@ -2154,7 +2361,7 @@ class TestOctetString(CommonMixin, TestCase):
             len_encode(len(b"".join(chunks))) +
             b"".join(chunks)
         )
             len_encode(len(b"".join(chunks))) +
             b"".join(chunks)
         )
-        with assertRaisesRegex(self, DecodeError, "unallowed BER"):
+        with self.assertRaisesRegex(DecodeError, "unallowed BER"):
             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
         for lenindef_expected, encoded in (
                 (True, encoded_indefinite),
             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
         for lenindef_expected, encoded in (
                 (True, encoded_indefinite),
@@ -2174,13 +2381,26 @@ class TestOctetString(CommonMixin, TestCase):
             self.assertEqual(obj.lenindef, lenindef_expected)
             self.assertTrue(obj.bered)
             self.assertEqual(len(encoded), obj.tlvlen)
             self.assertEqual(obj.lenindef, lenindef_expected)
             self.assertTrue(obj.bered)
             self.assertEqual(len(encoded), obj.tlvlen)
+            repr(obj)
+            list(obj.pps())
+            pprint(obj, big_blobs=True, with_decode_path=True)
+
+            evgens = list(OctetString(impl=tag_encode(impl)).decode_evgen(
+                encoded,
+                decode_path=decode_path,
+                ctx={"bered": True},
+            ))
+            self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
+            for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
+                self.assertGreater(len(dp), len(decode_path))
+                self.assertEqual(obj.vlen, chunk_len_expected)
 
     @given(
         integers(min_value=0),
         decode_path_strat,
     )
     def test_ber_definite_too_short(self, offset, decode_path):
 
     @given(
         integers(min_value=0),
         decode_path_strat,
     )
     def test_ber_definite_too_short(self, offset, decode_path):
-        with assertRaisesRegex(self, DecodeError, "longer than data") as err:
+        with self.assertRaisesRegex(DecodeError, "longer than data") as err:
             OctetString().decode(
                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
                 offset=offset,
             OctetString().decode(
                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
                 offset=offset,
@@ -2215,7 +2435,7 @@ class TestOctetString(CommonMixin, TestCase):
     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
         bs = OctetString(b"data").encode()
         bs_longer = OctetString(b"data-longer").encode()
     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
         bs = OctetString(b"data").encode()
         bs_longer = OctetString(b"data-longer").encode()
-        with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "chunk out of bounds") as err:
             OctetString().decode(
                 (
                     tag_encode(4, form=TagFormConstructed) +
             OctetString().decode(
                 (
                     tag_encode(4, form=TagFormConstructed) +
@@ -2230,6 +2450,24 @@ class TestOctetString(CommonMixin, TestCase):
         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
 
         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
 
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+    @given(integers(min_value=1001, max_value=3000))
+    def test_cer(self, data_len):
+        data = urandom(data_len)
+        encoded = encode_cer(OctetString(data))
+        ctx = {"bered": True}
+        self.assertSequenceEqual(bytes(OctetString().decod(encoded, ctx=ctx)), data)
+        evgens = list(OctetString().decode_evgen(encoded, ctx=ctx))
+        evgens_expected = data_len // 1000
+        if evgens_expected * 1000 != data_len:
+            evgens_expected += 1
+        evgens_expected += 1
+        self.assertEqual(len(evgens), evgens_expected)
+        for (_, obj, _) in evgens[:-2]:
+            self.assertEqual(obj.vlen, 1000)
+        _, obj, _ = evgens[-2]
+        self.assertEqual(obj.vlen, data_len - len(evgens[:-2]) * 1000)
+
 
 @composite
 def null_values_strategy(draw, do_expl=False):
 
 @composite
 def null_values_strategy(draw, do_expl=False):
@@ -2262,7 +2500,7 @@ class TestNull(CommonMixin, TestCase):
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
 
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
 
-    @given(binary(), binary())
+    @given(binary(min_size=1), binary(min_size=1))
     def test_comparison(self, tag1, tag2):
         for klass in (Null, NullInherited):
             obj1 = klass(impl=tag1)
     def test_comparison(self, tag1, tag2):
         for klass in (Null, NullInherited):
             obj1 = klass(impl=tag1)
@@ -2368,8 +2606,9 @@ class TestNull(CommonMixin, TestCase):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
     )
     )
-    def test_symmetric(self, values, tag_expl, offset, tail_junk):
+    def test_symmetric(self, values, tag_expl, offset, tail_junk, decode_path):
         for klass in (Null, NullInherited):
             _, _, optional, _decoded = values
             obj = klass(optional=optional, _decoded=_decoded)
         for klass in (Null, NullInherited):
             _, _, optional, _decoded = values
             obj = klass(optional=optional, _decoded=_decoded)
@@ -2378,12 +2617,20 @@ class TestNull(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
+            self.assertSequenceEqual(encode_cer(obj), obj_encoded)
             obj_expled = obj(expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
             obj_expled = obj(expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            obj_expled_cer = encode_cer(obj_expled)
+            self.assertNotEqual(obj_expled_cer, obj_encoded)
+            self.assertSequenceEqual(
+                obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+                obj_expled_encoded,
+            )
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -2417,6 +2664,21 @@ class TestNull(CommonMixin, TestCase):
                 tail_junk,
             )
 
                 tail_junk,
             )
 
+            evgens = list(obj_expled.decode_evgen(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj, obj_decoded)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
     @given(integers(min_value=1))
     def test_invalid_len(self, l):
         with self.assertRaises(InvalidLength):
     @given(integers(min_value=1))
     def test_invalid_len(self, l):
         with self.assertRaises(InvalidLength):
@@ -2433,8 +2695,8 @@ def oid_strategy(draw):
     if first_arc in (0, 1):
         second_arc = draw(integers(min_value=0, max_value=39))
     else:
     if first_arc in (0, 1):
         second_arc = draw(integers(min_value=0, max_value=39))
     else:
-        second_arc = draw(integers(min_value=0))
-    other_arcs = draw(lists(integers(min_value=0)))
+        second_arc = draw(integers(min_value=0, max_value=1 << 63))
+    other_arcs = draw(lists(integers(min_value=0, max_value=1 << 63)))
     return tuple([first_arc, second_arc] + other_arcs)
 
 
     return tuple([first_arc, second_arc] + other_arcs)
 
 
@@ -2484,6 +2746,8 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         obj = ObjectIdentifier(value)
         self.assertTrue(obj.ready)
         self.assertFalse(obj.ber_encoded)
         obj = ObjectIdentifier(value)
         self.assertTrue(obj.ready)
         self.assertFalse(obj.ber_encoded)
@@ -2492,7 +2756,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         pprint(obj, big_blobs=True, with_decode_path=True)
         hash(obj)
 
         pprint(obj, big_blobs=True, with_decode_path=True)
         hash(obj)
 
-    @given(oid_strategy(), oid_strategy(), binary(), binary())
+    @given(oid_strategy(), oid_strategy(), binary(min_size=1), binary(min_size=1))
     def test_comparison(self, value1, value2, tag1, tag2):
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
             obj1 = klass(value1)
     def test_comparison(self, value1, value2, tag1, tag2):
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
             obj1 = klass(value1)
@@ -2660,24 +2924,30 @@ class TestObjectIdentifier(CommonMixin, TestCase):
             len_encode(len(data)),
             data,
         ))
             len_encode(len(data)),
             data,
         ))
-        with assertRaisesRegex(self, DecodeError, "unfinished OID"):
+        with self.assertRaisesRegex(DecodeError, "unfinished OID"):
             obj.decode(data)
 
             obj.decode(data)
 
-    @given(integers(min_value=0))
+    @given(integers(min_value=0, max_value=1 << 63))
     def test_invalid_short(self, value):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((value,))
         with self.assertRaises(InvalidOID):
             ObjectIdentifier("%d" % value)
 
     def test_invalid_short(self, value):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((value,))
         with self.assertRaises(InvalidOID):
             ObjectIdentifier("%d" % value)
 
-    @given(integers(min_value=3), integers(min_value=0))
+    @given(
+        integers(min_value=3, max_value=1 << 63),
+        integers(min_value=0, max_value=1 << 63),
+    )
     def test_invalid_first_arc(self, first_arc, second_arc):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((first_arc, second_arc))
         with self.assertRaises(InvalidOID):
             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
 
     def test_invalid_first_arc(self, first_arc, second_arc):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((first_arc, second_arc))
         with self.assertRaises(InvalidOID):
             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
 
-    @given(integers(min_value=0, max_value=1), integers(min_value=40))
+    @given(
+        integers(min_value=0, max_value=1),
+        integers(min_value=40, max_value=1 << 63),
+    )
     def test_invalid_second_arc(self, first_arc, second_arc):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((first_arc, second_arc))
     def test_invalid_second_arc(self, first_arc, second_arc):
         with self.assertRaises(InvalidOID):
             ObjectIdentifier((first_arc, second_arc))
@@ -2705,8 +2975,9 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
             _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -2720,12 +2991,20 @@ class TestObjectIdentifier(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
+            self.assertSequenceEqual(encode_cer(obj), obj_encoded)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            obj_expled_cer = encode_cer(obj_expled)
+            self.assertNotEqual(obj_expled_cer, obj_encoded)
+            self.assertSequenceEqual(
+                obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
+                obj_expled_encoded,
+            )
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -2761,6 +3040,21 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 tail_junk,
             )
 
                 tail_junk,
             )
 
+            evgens = list(obj_expled.decode_evgen(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj, obj_decoded)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
     @given(
         oid_strategy().map(ObjectIdentifier),
         oid_strategy().map(ObjectIdentifier),
     @given(
         oid_strategy().map(ObjectIdentifier),
         oid_strategy().map(ObjectIdentifier),
@@ -2797,6 +3091,10 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 data,
             )))
 
                 data,
             )))
 
+    def test_go_non_minimal_encoding(self):
+        with self.assertRaises(DecodeError):
+            ObjectIdentifier().decode(hexdec("060a2a80864886f70d01010b"))
+
     def test_x690_vector(self):
         self.assertEqual(
             ObjectIdentifier().decode(hexdec("0603883703"))[0],
     def test_x690_vector(self):
         self.assertEqual(
             ObjectIdentifier().decode(hexdec("0603883703"))[0],
@@ -2816,9 +3114,31 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         obj = copy(obj)
         self.assertTrue(obj.ber_encoded)
         self.assertTrue(obj.bered)
         obj = copy(obj)
         self.assertTrue(obj.ber_encoded)
         self.assertTrue(obj.bered)
-        with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
+        with self.assertRaisesRegex(DecodeError, "non normalized arc encoding"):
             ObjectIdentifier().decode(tampered)
 
             ObjectIdentifier().decode(tampered)
 
+    @given(data_strategy())
+    def test_negative_arcs(self, d):
+        oid = list(d.draw(oid_strategy()))
+        if len(oid) == 2:
+            return
+        idx = d.draw(integers(min_value=3, max_value=len(oid)))
+        oid[idx - 1] *= -1
+        if oid[idx - 1] == 0:
+            oid[idx - 1] = -1
+        with self.assertRaises(InvalidOID):
+            ObjectIdentifier(tuple(oid))
+        with self.assertRaises(InvalidOID):
+            ObjectIdentifier(".".join(str(i) for i in oid))
+
+    @given(data_strategy())
+    def test_plused_arcs(self, d):
+        oid = [str(arc) for arc in d.draw(oid_strategy())]
+        idx = d.draw(integers(min_value=0, max_value=len(oid)))
+        oid[idx - 1] = "+" + oid[idx - 1]
+        with self.assertRaises(InvalidOID):
+            ObjectIdentifier(".".join(str(i) for i in oid))
+
     @given(data_strategy())
     def test_nonnormalized_arcs(self, d):
         arcs = d.draw(lists(
     @given(data_strategy())
     def test_nonnormalized_arcs(self, d):
         arcs = d.draw(lists(
@@ -2849,7 +3169,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         obj = copy(obj)
         self.assertTrue(obj.ber_encoded)
         self.assertTrue(obj.bered)
         obj = copy(obj)
         self.assertTrue(obj.ber_encoded)
         self.assertTrue(obj.bered)
-        with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
+        with self.assertRaisesRegex(DecodeError, "non normalized arc encoding"):
             ObjectIdentifier().decode(tampered)
 
 
             ObjectIdentifier().decode(tampered)
 
 
@@ -2887,7 +3207,7 @@ class TestEnumerated(CommonMixin, TestCase):
     base_klass = EWhatever
 
     def test_schema_required(self):
     base_klass = EWhatever
 
     def test_schema_required(self):
-        with assertRaisesRegex(self, ValueError, "schema must be specified"):
+        with self.assertRaisesRegex(ValueError, "schema must be specified"):
             Enumerated()
 
     def test_invalid_value_type(self):
             Enumerated()
 
     def test_invalid_value_type(self):
@@ -2940,7 +3260,7 @@ class TestEnumerated(CommonMixin, TestCase):
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
 
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
 
-    @given(integers(), integers(), binary(), binary())
+    @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
     def test_comparison(self, value1, value2, tag1, tag2):
         class E(Enumerated):
             schema = (
     def test_comparison(self, value1, value2, tag1, tag2):
         class E(Enumerated):
             schema = (
@@ -3056,6 +3376,7 @@ class TestEnumerated(CommonMixin, TestCase):
         offset = d.draw(integers(min_value=0))
         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
         tail_junk = d.draw(binary(max_size=5))
         offset = d.draw(integers(min_value=0))
         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
         tail_junk = d.draw(binary(max_size=5))
+        decode_path = d.draw(decode_path_strat)
 
         class E(Enumerated):
             schema = schema_input
 
         class E(Enumerated):
             schema = schema_input
@@ -3070,6 +3391,7 @@ class TestEnumerated(CommonMixin, TestCase):
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
+        self.assertEqual(encode2pass(obj), obj_encoded)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         repr(obj_expled)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         repr(obj_expled)
@@ -3111,6 +3433,21 @@ class TestEnumerated(CommonMixin, TestCase):
             tail_junk,
         )
 
             tail_junk,
         )
 
+        evgens = list(obj_expled.decode_evgen(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+            decode_path=decode_path,
+            ctx=ctx_copied,
+        ))
+        self.assertEqual(len(evgens), 1)
+        _decode_path, obj, tail = evgens[0]
+        self.assertSequenceEqual(tail, tail_junk)
+        self.assertEqual(_decode_path, decode_path)
+        self.assertEqual(obj, obj_decoded)
+        self.assertEqual(obj.expl_offset, offset)
+        repr(obj)
+        list(obj.pps())
+
 
 @composite
 def string_values_strategy(draw, alphabet, do_expl=False):
 
 @composite
 def string_values_strategy(draw, alphabet, do_expl=False):
@@ -3152,9 +3489,7 @@ class StringMixin(object):
         repr(err.exception)
 
     def text_alphabet(self):
         repr(err.exception)
 
     def text_alphabet(self):
-        if self.base_klass.encoding in ("ascii", "iso-8859-1"):
-            return printable + whitespace
-        return None
+        return "".join(chr(c) for c in range(256))
 
     @given(booleans())
     def test_optional(self, optional):
 
     @given(booleans())
     def test_optional(self, optional):
@@ -3168,17 +3503,19 @@ class StringMixin(object):
         repr(obj)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
         repr(obj)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
-        text_type(obj)
+        str(obj)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         value = d.draw(text(alphabet=self.text_alphabet()))
         obj = self.base_klass(value)
         self.assertTrue(obj.ready)
         repr(obj)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
         value = d.draw(text(alphabet=self.text_alphabet()))
         obj = self.base_klass(value)
         self.assertTrue(obj.ready)
         repr(obj)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
-        text_type(obj)
+        str(obj)
 
     @given(data_strategy())
     def test_comparison(self, d):
 
     @given(data_strategy())
     def test_comparison(self, d):
@@ -3191,7 +3528,7 @@ class StringMixin(object):
         self.assertEqual(obj1 == obj2, value1 == value2)
         self.assertEqual(obj1 != obj2, value1 != value2)
         self.assertEqual(obj1 == bytes(obj2), value1 == 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)
+        self.assertEqual(obj1 == str(obj2), value1 == value2)
         obj1 = self.base_klass(value1, impl=tag1)
         obj2 = self.base_klass(value1, impl=tag2)
         self.assertEqual(obj1 == obj2, tag1 == tag2)
         obj1 = self.base_klass(value1, impl=tag1)
         obj2 = self.base_klass(value1, impl=tag2)
         self.assertEqual(obj1 == obj2, tag1 == tag2)
@@ -3216,20 +3553,28 @@ class StringMixin(object):
         with self.assertRaises(BoundsError) as err:
             self.base_klass(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
         with self.assertRaises(BoundsError) as err:
             self.base_klass(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 self.base_klass(value).encode()
             )
         repr(err.exception)
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 self.base_klass(value).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            self.base_klass(bounds=(bound_min, bound_max)).decode(
+                encode2pass(self.base_klass(value))
+            )
         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
         with self.assertRaises(BoundsError) as err:
             self.base_klass(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
         with self.assertRaises(BoundsError) as err:
             self.base_klass(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 self.base_klass(value).encode()
             )
         repr(err.exception)
             self.base_klass(bounds=(bound_min, bound_max)).decode(
                 self.base_klass(value).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            self.base_klass(bounds=(bound_min, bound_max)).decode(
+                encode2pass(self.base_klass(value))
+            )
 
     @given(data_strategy())
     def test_call(self, d):
 
     @given(data_strategy())
     def test_call(self, d):
@@ -3392,6 +3737,7 @@ class StringMixin(object):
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         tail_junk = d.draw(binary(max_size=5))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         tail_junk = d.draw(binary(max_size=5))
+        decode_path = d.draw(decode_path_strat)
         _, _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
         _, _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
@@ -3404,6 +3750,7 @@ class StringMixin(object):
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
+        self.assertEqual(encode2pass(obj), obj_encoded)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         repr(obj_expled)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         repr(obj_expled)
@@ -3425,8 +3772,8 @@ class StringMixin(object):
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
         self.assertEqual(bytes(obj_decoded), bytes(obj))
         self.assertNotEqual(obj_decoded, obj)
         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
         self.assertEqual(bytes(obj_decoded), bytes(obj))
-        self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
-        self.assertEqual(text_type(obj_decoded), text_type(obj))
+        self.assertEqual(str(obj_decoded), str(obj_expled))
+        self.assertEqual(str(obj_decoded), str(obj))
         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
@@ -3447,18 +3794,50 @@ class StringMixin(object):
             tail_junk,
         )
 
             tail_junk,
         )
 
-
-class TestUTF8String(StringMixin, CommonMixin, TestCase):
-    base_klass = UTF8String
+        evgens = list(obj_expled.decode_evgen(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+            decode_path=decode_path,
+            ctx=ctx_copied,
+        ))
+        self.assertEqual(len(evgens), 1)
+        _decode_path, obj, tail = evgens[0]
+        self.assertSequenceEqual(tail, tail_junk)
+        self.assertEqual(_decode_path, decode_path)
+        if not getattr(self, "evgen_mode_skip_value", True):
+            self.assertEqual(obj, obj_decoded)
+        self.assertEqual(obj.expl_offset, offset)
+        repr(obj)
+        list(obj.pps())
 
 
 cyrillic_letters = text(
 
 
 cyrillic_letters = text(
-    alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
+    alphabet="".join(chr(i) for i in list(range(0x0410, 0x044f + 1))),
     min_size=1,
     max_size=5,
 )
 
 
     min_size=1,
     max_size=5,
 )
 
 
+class TestUTF8String(StringMixin, CommonMixin, TestCase):
+    base_klass = UTF8String
+
+    @given(cyrillic_letters)
+    def test_byte_per_primitive(self, chars):
+        char = chars[0]
+        char_raw = char.encode("utf-8")
+        encoded = b"".join((
+            self.base_klass().tag_constructed,
+            LENINDEF,
+            OctetString(char_raw[:1]).encode(),
+            OctetString(char_raw[1:2]).encode(),
+            EOC,
+        ))
+        self.assertEqual(
+            self.base_klass().decod(encoded, ctx={"bered": True}),
+            char,
+        )
+
+
 class UnicodeDecodeErrorMixin(object):
     @given(cyrillic_letters)
     def test_unicode_decode_error(self, cyrillic_text):
 class UnicodeDecodeErrorMixin(object):
     @given(cyrillic_letters)
     def test_unicode_decode_error(self, cyrillic_text):
@@ -3474,7 +3853,7 @@ class TestNumericString(StringMixin, CommonMixin, TestCase):
 
     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
     def test_non_numeric(self, non_numeric_text):
 
     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
     def test_non_numeric(self, non_numeric_text):
-        with assertRaisesRegex(self, DecodeError, "non-numeric"):
+        with self.assertRaisesRegex(DecodeError, "alphabet value"):
             self.base_klass(non_numeric_text)
 
     @given(
             self.base_klass(non_numeric_text)
 
     @given(
@@ -3497,6 +3876,19 @@ class TestNumericString(StringMixin, CommonMixin, TestCase):
         self.assertEqual(err.exception.offset, offset)
         self.assertEqual(err.exception.decode_path, decode_path)
 
         self.assertEqual(err.exception.offset, offset)
         self.assertEqual(err.exception.decode_path, decode_path)
 
+    def test_byte_per_primitive(self):
+        encoded = b"".join((
+            self.base_klass().tag_constructed,
+            LENINDEF,
+            OctetString(b"1").encode(),
+            OctetString(b"2").encode(),
+            EOC,
+        ))
+        self.assertEqual(
+            self.base_klass().decod(encoded, ctx={"bered": True}),
+            "12",
+        )
+
 
 class TestPrintableString(
         UnicodeDecodeErrorMixin,
 
 class TestPrintableString(
         UnicodeDecodeErrorMixin,
@@ -3511,7 +3903,7 @@ class TestPrintableString(
 
     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
     def test_non_printable(self, non_printable_text):
 
     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
     def test_non_printable(self, non_printable_text):
-        with assertRaisesRegex(self, DecodeError, "non-printable"):
+        with self.assertRaisesRegex(DecodeError, "alphabet value"):
             self.base_klass(non_printable_text)
 
     @given(
             self.base_klass(non_printable_text)
 
     @given(
@@ -3540,14 +3932,22 @@ class TestPrintableString(
                 ("&", {"allow_ampersand": True}),
                 ("&*", {"allow_asterisk": True, "allow_ampersand": True}),
         ):
                 ("&", {"allow_ampersand": True}),
                 ("&*", {"allow_asterisk": True, "allow_ampersand": True}),
         ):
-            s = "hello invalid " + c
-            with assertRaisesRegex(self, DecodeError, "non-printable"):
+            s = "hello invalid"
+            obj = self.base_klass(s)
+            for prop in kwargs.keys():
+                self.assertFalse(getattr(obj, prop))
+            s += c
+            with self.assertRaisesRegex(DecodeError, "alphabet value"):
                 self.base_klass(s)
             self.base_klass(s, **kwargs)
             klass = self.base_klass(**kwargs)
             obj = klass(s)
                 self.base_klass(s)
             self.base_klass(s, **kwargs)
             klass = self.base_klass(**kwargs)
             obj = klass(s)
+            for prop in kwargs.keys():
+                self.assertTrue(getattr(obj, prop))
             obj = copy(obj)
             obj(s)
             obj = copy(obj)
             obj(s)
+            for prop in kwargs.keys():
+                self.assertTrue(getattr(obj, prop))
 
 
 class TestTeletexString(
 
 
 class TestTeletexString(
@@ -3576,6 +3976,18 @@ class TestIA5String(
 ):
     base_klass = IA5String
 
 ):
     base_klass = IA5String
 
+    def text_alphabet(self):
+        return "".join(chr(c) for c in range(128))
+
+    @given(integers(min_value=128, max_value=255))
+    def test_alphabet_bad(self, code):
+        with self.assertRaises(DecodeError):
+            self.base_klass().decod(
+                self.base_klass.tag_default +
+                len_encode(1) +
+                bytes(bytearray([code])),
+            )
+
 
 class TestGraphicString(
         UnicodeDecodeErrorMixin,
 
 class TestGraphicString(
         UnicodeDecodeErrorMixin,
@@ -3594,6 +4006,9 @@ class TestVisibleString(
 ):
     base_klass = VisibleString
 
 ):
     base_klass = VisibleString
 
+    def text_alphabet(self):
+        return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
+
     def test_x690_vector(self):
         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
         self.assertSequenceEqual(tail, b"")
     def test_x690_vector(self):
         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
         self.assertSequenceEqual(tail, b"")
@@ -3630,6 +4045,38 @@ class TestVisibleString(
         self.assertTrue(obj.lenindef)
         self.assertTrue(obj.bered)
 
         self.assertTrue(obj.lenindef)
         self.assertTrue(obj.bered)
 
+    @given(one_of((
+        integers(min_value=0, max_value=ord(" ") - 1),
+        integers(min_value=ord("~") + 1, max_value=255),
+    )))
+    def test_alphabet_bad(self, code):
+        with self.assertRaises(DecodeError):
+            self.base_klass().decod(
+                self.base_klass.tag_default +
+                len_encode(1) +
+                bytes(bytearray([code])),
+            )
+
+    @given(
+        sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
+        integers(min_value=0),
+        decode_path_strat,
+    )
+    def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
+        value, bound_min = list(sorted(ints))
+
+        class String(self.base_klass):
+            bounds = (bound_min, bound_min)
+        with self.assertRaises(DecodeError) as err:
+            String().decode(
+                self.base_klass(b"1" * value).encode(),
+                offset=offset,
+                decode_path=decode_path,
+            )
+        repr(err.exception)
+        self.assertEqual(err.exception.offset, offset)
+        self.assertEqual(err.exception.decode_path, decode_path)
+
 
 class TestGeneralString(
         UnicodeDecodeErrorMixin,
 
 class TestGeneralString(
         UnicodeDecodeErrorMixin,
@@ -3707,7 +4154,12 @@ class TimeMixin(object):
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
-        value = d.draw(datetimes(min_value=self.min_datetime))
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
+        value = d.draw(datetimes(
+            min_value=self.min_datetime,
+            max_value=self.max_datetime,
+        ))
         obj = self.base_klass(value)
         self.assertTrue(obj.ready)
         repr(obj)
         obj = self.base_klass(value)
         self.assertTrue(obj.ready)
         repr(obj)
@@ -3862,6 +4314,7 @@ class TimeMixin(object):
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
+        self.assertEqual(encode2pass(obj), obj_encoded)
         self.additional_symmetric_check(value, obj_encoded)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         self.additional_symmetric_check(value, obj_encoded)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
@@ -3909,11 +4362,16 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
     omit_ms = False
     min_datetime = datetime(1900, 1, 1)
     max_datetime = datetime(9999, 12, 31)
     omit_ms = False
     min_datetime = datetime(1900, 1, 1)
     max_datetime = datetime(9999, 12, 31)
+    evgen_mode_skip_value = False
 
     def additional_symmetric_check(self, value, obj_encoded):
         if value.microsecond > 0:
             self.assertFalse(obj_encoded.endswith(b"0Z"))
 
 
     def additional_symmetric_check(self, value, obj_encoded):
         if value.microsecond > 0:
             self.assertFalse(obj_encoded.endswith(b"0Z"))
 
+    def test_repr_not_ready(self):
+        str(GeneralizedTime())
+        repr(GeneralizedTime())
+
     def test_x690_vector_valid(self):
         for data in ((
                 b"19920521000000Z",
     def test_x690_vector_valid(self):
         for data in ((
                 b"19920521000000Z",
@@ -3966,6 +4424,243 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
             datetime(2010, 1, 2, 3, 4, 5, 0),
         )
 
             datetime(2010, 1, 2, 3, 4, 5, 0),
         )
 
+    def test_go_vectors_valid_ber(self):
+        for data in ((
+                b"20100102030405+0607",
+                b"20100102030405-0607",
+        )):
+            GeneralizedTime(data, ctx={"bered": True})
+
+    def test_utc_offsets(self):
+        """Some know equal UTC offsets
+        """
+        dts = [
+            GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
+            for data in (
+                "200101011830Z",
+                "200101012230+04",
+                "200101011130-0700",
+                "200101011500-03:30",
+            )
+        ]
+        self.assertEqual(dts[0], dts[1])
+        self.assertEqual(dts[0], dts[2])
+        self.assertEqual(dts[0], dts[3])
+
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+    @given(data_strategy())
+    def test_valid_ber(self, d):
+        year = d.draw(integers(min_value=2, max_value=9999))
+        month = d.draw(integers(min_value=1, max_value=12))
+        day = d.draw(integers(min_value=1, max_value=28))
+        hours = d.draw(integers(min_value=0, max_value=23))
+        data = "%04d%02d%02d%02d" % (year, month, day, hours)
+        dt = datetime(year, month, day, hours)
+        fractions_sign = d.draw(sampled_from("  ,."))
+        fractions = None
+        if fractions_sign != " ":
+            fractions = random()
+        if d.draw(booleans()):
+            minutes = d.draw(integers(min_value=0, max_value=59))
+            data += "%02d" % minutes
+            dt += timedelta(seconds=60 * minutes)
+            if d.draw(booleans()):
+                seconds = d.draw(integers(min_value=0, max_value=59))
+                data += "%02d" % seconds
+                dt += timedelta(seconds=seconds)
+                if fractions is not None:
+                    dt += timedelta(microseconds=10**6 * fractions)
+            elif fractions is not None:
+                dt += timedelta(seconds=60 * fractions)
+        elif fractions is not None:
+            dt += timedelta(seconds=3600 * fractions)
+        if fractions is not None:
+            data += fractions_sign + str(fractions)[2:]
+        if d.draw(booleans()):
+            data += "Z"
+        elif d.draw(booleans()):
+            offset_hour = d.draw(integers(min_value=0, max_value=13))
+            sign = 1
+            if d.draw(booleans()):
+                data += "-"
+                sign = -1
+            else:
+                data += "+"
+            dt -= timedelta(seconds=sign * 3600 * offset_hour)
+            data += "%02d" % offset_hour
+            minutes_separator = d.draw(sampled_from((None, "", ":")))
+            if minutes_separator is not None:
+                offset_minute = d.draw(integers(min_value=0, max_value=59))
+                dt -= timedelta(seconds=sign * 60 * offset_minute)
+                data += "%s%02d" % (minutes_separator, offset_minute)
+        data = data.encode("ascii")
+        data_der = GeneralizedTime.tag_default + len_encode(len(data)) + data
+        try:
+            GeneralizedTime().decod(data_der)
+        except DecodeError:
+            dered = False
+        else:
+            dered = True
+        obj = GeneralizedTime().decod(data_der, ctx={"bered": True})
+        if dt.year > 1970:
+            self.assertEqual(
+                mktime(obj.todatetime().timetuple()),
+                mktime(dt.timetuple()),
+            )
+        else:
+            try:
+                obj.todatetime().timestamp()
+            except:
+                pass
+            else:
+                self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
+        self.assertEqual(obj.ber_encoded, not dered)
+        self.assertEqual(obj.bered, not dered)
+        self.assertEqual(obj.ber_raw, None if dered else data)
+        self.assertEqual(obj.encode() == data_der, dered)
+        repr(obj)
+        bytes(obj)
+        str(obj)
+
+    def test_invalid_ber(self):
+        for data in ((
+                # "00010203040506.07",
+                "-0010203040506.07",
+                "0001-203040506.07",
+                "000102-3040506.07",
+                "00010203-40506.07",
+                "0001020304-506.07",
+                "000102030405-6.07",
+                "00010203040506.-7",
+                "+0010203040506.07",
+                "0001+203040506.07",
+                "000102+3040506.07",
+                "00010203+40506.07",
+                "0001020304+506.07",
+                "000102030405+6.07",
+                "00010203040506.+7",
+                " 0010203040506.07",
+                "0001 203040506.07",
+                "000102 3040506.07",
+                "00010203 40506.07",
+                "0001020304 506.07",
+                "000102030405 6.07",
+                "00010203040506. 7",
+                "001 0203040506.07",
+                "00012 03040506.07",
+                "0001023 040506.07",
+                "000102034 0506.07",
+                "00010203045 06.07",
+                "0001020304056 .07",
+                "00010203040506.7 ",
+                "00010203040506.",
+                "0001020304050607",
+
+                "-0010203040506",
+                "0001-203040506",
+                "000102-3040506",
+                "00010203-40506",
+                "0001020304-506",
+                "000102030405-6",
+                "0001+203040506",
+                "000102+3040506",
+                "00010203+40506",
+                "0001020304+506",
+                "000102030405+6",
+                " 0010203040506",
+                "0001 203040506",
+                "000102 3040506",
+                "00010203 40506",
+                "0001020304 506",
+                "000102030405 6",
+                "001 0203040506",
+                "00012 03040506",
+                "0001023 040506",
+                "000102034 0506",
+                "00010203045 06",
+                "0001020304056 ",
+
+                "-00102030405.07",
+                "0001-2030405.07",
+                "000102-30405.07",
+                "00010203-405.07",
+                "0001020304-5.07",
+                "000102030405.-7",
+                "+00102030405.07",
+                "0001+2030405.07",
+                "00010203+405.07",
+                "0001020304+5.07",
+                "000102030405.+7",
+                " 00102030405.07",
+                "0001 2030405.07",
+                "000102 30405.07",
+                "00010203 405.07",
+                "0001020304 5.07",
+                "000102030405. 7",
+                "001 02030405.07",
+                "00012 030405.07",
+                "0001023 0405.07",
+                "000102034 05.07",
+                "00010203045 .07",
+                "000102030405.7 ",
+                "000102030405.",
+
+                "-001020304.07",
+                "0001-20304.07",
+                "000102-304.07",
+                "00010203-4.07",
+                "0001020304.-7",
+                "+001020304.07",
+                "0001+20304.07",
+                "00010203+4.07",
+                "0001020304.+7",
+                " 001020304.07",
+                "0001 20304.07",
+                "000102 304.07",
+                "00010203 4.07",
+                "0001020304. 7",
+                "001 020304.07",
+                "00012 0304.07",
+                "0001023 04.07",
+                "000102034 .07",
+                "0001020304.7 ",
+                "0001020304.",
+
+                "00010203",
+                "00010203040506Y",
+                "0001010100+0001",
+                "0001010100+00:01",
+                "0001010100+01",
+
+                "00010203040506.07+15",
+                "00010203040506.07-15",
+                "00010203040506.07+14:60",
+                "00010203040506.07+1460",
+                "00010203040506.07-1460",
+                "00010203040506.07+00:60",
+                "00010203040506.07-00:60",
+
+                "00010203040506+15",
+                "00010203040506-15",
+                "00010203040506+14:60",
+                "00010203040506+1460",
+                "00010203040506-1460",
+                "00010203040506+00:60",
+                "00010203040506-00:60",
+
+                "0001020304050.07",
+                "00010203040.07",
+                "000102030.07",
+                "0001020304050",
+                "00010203040",
+                "000102030",
+        )):
+            with self.assertRaises(DecodeError):
+                GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
+            data = data.replace(".", ",")
+            with self.assertRaises(DecodeError):
+                GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
+
     @given(
         binary(
             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
     @given(
         binary(
             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
@@ -4010,19 +4705,65 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
 
     def test_ns_fractions(self):
         GeneralizedTime(b"20010101000000.000001Z")
 
     def test_ns_fractions(self):
         GeneralizedTime(b"20010101000000.000001Z")
-        with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
+        with self.assertRaisesRegex(DecodeError, "only microsecond fractions"):
             GeneralizedTime(b"20010101000000.0000001Z")
 
             GeneralizedTime(b"20010101000000.0000001Z")
 
+    def test_non_pure_integers(self):
+        for data in ((
+                # b"20000102030405Z,
+                b"+2000102030405Z",
+                b"2000+102030405Z",
+                b"200001+2030405Z",
+                b"20000102+30405Z",
+                b"2000010203+405Z",
+                b"200001020304+5Z",
+                b"20000102030405.+6Z",
+                b"20000102030405.-6Z",
+                b"_2000102030405Z",
+                b"2000_102030405Z",
+                b"200001_2030405Z",
+                b"20000102_30405Z",
+                b"2000010203_405Z",
+                b"200001020304_5Z",
+                b"20000102030405._6Z",
+                b"20000102030405.6_Z",
+                b" 2000102030405Z",
+                b"2000 102030405Z",
+                b"200001 2030405Z",
+                b"20000102 30405Z",
+                b"2000010203 405Z",
+                b"200001020304 5Z",
+                b"20000102030405. 6Z",
+                b"200 0102030405Z",
+                b"20001 02030405Z",
+                b"2000012 030405Z",
+                b"200001023 0405Z",
+                b"20000102034 05Z",
+                b"2000010203045 Z",
+                b"20000102030405.6 Z",
+        )):
+            with self.assertRaises(DecodeError):
+                GeneralizedTime(data)
+
+    def test_aware(self):
+        with self.assertRaisesRegex(ValueError, "only naive"):
+            GeneralizedTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
+
 
 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
     base_klass = UTCTime
     omit_ms = True
     min_datetime = datetime(2000, 1, 1)
     max_datetime = datetime(2049, 12, 31)
 
 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
     base_klass = UTCTime
     omit_ms = True
     min_datetime = datetime(2000, 1, 1)
     max_datetime = datetime(2049, 12, 31)
+    evgen_mode_skip_value = False
 
     def additional_symmetric_check(self, value, obj_encoded):
         pass
 
 
     def additional_symmetric_check(self, value, obj_encoded):
         pass
 
+    def test_repr_not_ready(self):
+        str(GeneralizedTime())
+        repr(UTCTime())
+
     def test_x690_vector_valid(self):
         for data in ((
                 b"920521000000Z",
     def test_x690_vector_valid(self):
         for data in ((
                 b"920521000000Z",
@@ -4083,6 +4824,229 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
             datetime(1991, 5, 6, 23, 45, 40, 0),
         )
 
             datetime(1991, 5, 6, 23, 45, 40, 0),
         )
 
+    def test_non_pure_integers(self):
+        for data in ((
+                # b"000102030405Z",
+                b"+10102030405Z",
+                b"00+102030405Z",
+                b"0001+2030405Z",
+                b"000102+30405Z",
+                b"00010203+405Z",
+                b"0001020304+5Z",
+                b"_10102030405Z",
+                b"00_102030405Z",
+                b"0001_2030405Z",
+                b"000102_30405Z",
+                b"00010203_405Z",
+                b"0001020304_5Z",
+                b"00010203045_Z",
+                b" 10102030405Z",
+                b"00 102030405Z",
+                b"0001 2030405Z",
+                b"000102 30405Z",
+                b"00010203 405Z",
+                b"0001020304 5Z",
+                b"1 0102030405Z",
+                b"001 02030405Z",
+                b"00012 030405Z",
+                b"0001023 0405Z",
+                b"000102034 05Z",
+                b"00010203045 Z",
+        )):
+            with self.assertRaises(DecodeError):
+                UTCTime(data)
+
+    def test_x680_vector_valid_ber(self):
+        for data, dt in ((
+                (b"8201021200Z", datetime(1982, 1, 2, 12)),
+                (b"8201020700-0500", datetime(1982, 1, 2, 12)),
+                (b"0101021200Z", datetime(2001, 1, 2, 12)),
+                (b"0101020700-0500", datetime(2001, 1, 2, 12)),
+        )):
+            data_der = UTCTime.tag_default + len_encode(len(data)) + data
+            obj = UTCTime().decod(data_der, ctx={"bered": True})
+            self.assertEqual(obj, dt)
+            self.assertEqual(obj.todatetime(), dt)
+            self.assertTrue(obj.ber_encoded)
+            self.assertTrue(obj.bered)
+            self.assertEqual(obj.ber_raw, data)
+            self.assertNotEqual(obj.encode(), data_der)
+            repr(obj)
+
+    def test_go_vectors_valid_ber(self):
+        for data in ((
+                b"910506164540-0700",
+                b"910506164540+0730",
+                b"9105062345Z",
+                b"5105062345Z",
+        )):
+            data = UTCTime.tag_default + len_encode(len(data)) + data
+            obj = UTCTime().decod(data, ctx={"bered": True})
+            self.assertTrue(obj.ber_encoded)
+            self.assertTrue(obj.bered)
+            self.assertNotEqual(obj.encode(), data)
+            repr(obj)
+
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+    @given(data_strategy())
+    def test_valid_ber(self, d):
+        year = d.draw(integers(min_value=0, max_value=99))
+        month = d.draw(integers(min_value=1, max_value=12))
+        day = d.draw(integers(min_value=1, max_value=28))
+        hours = d.draw(integers(min_value=0, max_value=23))
+        minute = d.draw(integers(min_value=0, max_value=59))
+        data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
+        dt = datetime(
+            year + (2000 if year < 50 else 1900),
+            month,
+            day,
+            hours,
+            minute,
+        )
+        dered = False
+        if d.draw(booleans()):
+            dered = True
+            seconds = d.draw(integers(min_value=0, max_value=59))
+            data += "%02d" % seconds
+            dt += timedelta(seconds=seconds)
+        if d.draw(booleans()):
+            data += "Z"
+        else:
+            dered = False
+            offset_hour = d.draw(integers(min_value=0, max_value=13))
+            offset_minute = d.draw(integers(min_value=0, max_value=59))
+            offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
+            if d.draw(booleans()):
+                dt += offset
+                data += "-"
+            else:
+                dt -= offset
+                data += "+"
+            data += "%02d%02d" % (offset_hour, offset_minute)
+        data = data.encode("ascii")
+        data_der = UTCTime.tag_default + len_encode(len(data)) + data
+        obj = UTCTime().decod(data_der, ctx={"bered": True})
+        self.assertEqual(obj, dt)
+        self.assertEqual(obj.todatetime(), dt)
+        self.assertEqual(obj.ber_encoded, not dered)
+        self.assertEqual(obj.bered, not dered)
+        self.assertEqual(obj.ber_raw, None if dered else data)
+        self.assertEqual(obj.encode() == data_der, dered)
+        repr(obj)
+        bytes(obj)
+        str(obj)
+
+    def test_invalid_ber(self):
+        for data in ((
+                # b"0001020304Z",
+                b"-101020304Z",
+                b"00-1020304Z",
+                b"0001-20304Z",
+                b"000102-304Z",
+                b"000102-104Z",
+                b"00000203-4Z",
+                b"+101020304Z",
+                b"00+1020304Z",
+                b"0001+20304Z",
+                b"000102+304Z",
+                b"000102+104Z",
+                b"00000203+4Z",
+                b" 101020304Z",
+                b"00 1020304Z",
+                b"0001 20304Z",
+                b"000102 304Z",
+                b"000102 104Z",
+                b"00000203 4Z",
+                b"1 01020304Z",
+                b"001 020304Z",
+                b"00012 0304Z",
+                b"0001023 04Z",
+                b"0001021 04Z",
+                b"000002034 Z",
+                b"0013020304Z",
+                b"0001000304Z",
+                b"0001320304Z",
+                b"0001022404Z",
+                b"0001020360Z",
+                b"0002300304Z",
+                b"0001020304",
+                b"0001020304T",
+                b"0001020304+",
+                b"0001020304-",
+                b"0001020304+0",
+                b"0001020304+00",
+                b"0001020304+000",
+                b"0001020304+000Z",
+                b"0001020304+0000Z",
+                b"0001020304+-101",
+                b"0001020304+01-1",
+                b"0001020304+0060",
+                b"0001020304+1401",
+                b"5001010000+0001",
+                b"000102030Z",
+                b"0001020Z",
+        )):
+            with self.assertRaises(DecodeError):
+                UTCTime(data, ctx={"bered": True})
+            data = data[:8] + data[8+2:]
+            with self.assertRaises(DecodeError):
+                UTCTime(data, ctx={"bered": True})
+
+        for data in ((
+                # b"000102030405Z",
+                b"-10102030405Z",
+                b"00-102030405Z",
+                b"0001-2030405Z",
+                b"000102-30405Z",
+                b"000102-10405Z",
+                b"00000203-405Z",
+                b"0000020304-5Z",
+                b"+10102030405Z",
+                b"00+102030405Z",
+                b"0001+2030405Z",
+                b"000102+30405Z",
+                b"000102+10405Z",
+                b"00000203+405Z",
+                b"0000020304+5Z",
+                b" 10102030405Z",
+                b"00 102030405Z",
+                b"0001 2030405Z",
+                b"000102 30405Z",
+                b"000102 10405Z",
+                b"00000203 405Z",
+                b"0000020304 5Z",
+                b"1 0102030405Z",
+                b"001 02030405Z",
+                b"00012 030405Z",
+                b"0001023 0405Z",
+                b"0001021 0405Z",
+                b"000002034 05Z",
+                b"00000203045 Z",
+                b"001302030405Z",
+                b"000100030405Z",
+                b"000132030405Z",
+                b"000102240405Z",
+                b"000102036005Z",
+                b"000230030405Z",
+                b"000102030460Z",
+                b"000102030405",
+                b"000102030405T",
+                b"000102030405+",
+                b"000102030405-",
+                b"000102030405+0",
+                b"000102030405+00",
+                b"000102030405+000",
+                b"000102030405+000Z",
+                b"000102030405+0000Z",
+                b"000102030405+-101",
+                b"000102030405+01-1",
+                b"000102030405+0060",
+                b"000102030405+1401",
+                b"500101000002+0003",
+        )):
+            with self.assertRaises(DecodeError):
+                UTCTime(data, ctx={"bered": True})
+
     @given(integers(min_value=0, max_value=49))
     def test_pre50(self, year):
         self.assertEqual(
     @given(integers(min_value=0, max_value=49))
     def test_pre50(self, year):
         self.assertEqual(
@@ -4118,10 +5082,29 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
                 junk
             )
 
                 junk
             )
 
+    def test_aware(self):
+        with self.assertRaisesRegex(ValueError, "only naive"):
+            UTCTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
+
+    def test_raises_if_no_dateutil(self):
+        with patch("pyderasn.tzUTC", new="missing"):
+            with self.assertRaisesRegex(NotImplementedError, "dateutil"):
+                UTCTime(datetime.now()).totzdatetime()
+
+    def test_tzinfo_gives_datetime_with_tzutc_tzinfo(self):
+        self.assertEqual(UTCTime(datetime.now()).totzdatetime().tzinfo, UTC)
+
+
+@composite
+def tlv_value_strategy(draw):
+    tag_num = draw(integers(min_value=1))
+    data = draw(binary())
+    return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
+
 
 @composite
 def any_values_strategy(draw, do_expl=False):
 
 @composite
 def any_values_strategy(draw, do_expl=False):
-    value = draw(one_of(none(), binary()))
+    value = draw(one_of(none(), tlv_value_strategy()))
     expl = None
     if do_expl:
         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
     expl = None
     if do_expl:
         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
@@ -4151,7 +5134,7 @@ class TestAny(CommonMixin, TestCase):
         obj = Any(optional=optional)
         self.assertEqual(obj.optional, optional)
 
         obj = Any(optional=optional)
         self.assertEqual(obj.optional, optional)
 
-    @given(binary())
+    @given(tlv_value_strategy())
     def test_ready(self, value):
         obj = Any()
         self.assertFalse(obj.ready)
     def test_ready(self, value):
         obj = Any()
         self.assertFalse(obj.ready)
@@ -4161,6 +5144,8 @@ class TestAny(CommonMixin, TestCase):
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         obj = Any(value)
         self.assertTrue(obj.ready)
         repr(obj)
         obj = Any(value)
         self.assertTrue(obj.ready)
         repr(obj)
@@ -4185,7 +5170,7 @@ class TestAny(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertSequenceEqual(obj.encode(), integer_encoded)
 
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertSequenceEqual(obj.encode(), integer_encoded)
 
-    @given(binary(), binary())
+    @given(tlv_value_strategy(), tlv_value_strategy())
     def test_comparison(self, value1, value2):
         for klass in (Any, AnyInherited):
             obj1 = klass(value1)
     def test_comparison(self, value1, value2):
         for klass in (Any, AnyInherited):
             obj1 = klass(value1)
@@ -4249,7 +5234,7 @@ class TestAny(CommonMixin, TestCase):
             obj.decode(obj.encode()[:-1])
 
     @given(
             obj.decode(obj.encode()[:-1])
 
     @given(
-        binary(),
+        tlv_value_strategy(),
         integers(min_value=1).map(tag_ctxc),
     )
     def test_stripped_expl(self, value, tag_expl):
         integers(min_value=1).map(tag_ctxc),
     )
     def test_stripped_expl(self, value, tag_expl):
@@ -4296,8 +5281,19 @@ class TestAny(CommonMixin, TestCase):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
+        booleans(),
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
+    def test_symmetric(
+            self,
+            values,
+            value,
+            tag_expl,
+            offset,
+            tail_junk,
+            decode_path,
+            keep_memoryview,
+    ):
         for klass in (Any, AnyInherited):
             _, _, optional, _decoded = values
             obj = klass(value=value, optional=optional, _decoded=_decoded)
         for klass in (Any, AnyInherited):
             _, _, optional, _decoded = values
             obj = klass(value=value, optional=optional, _decoded=_decoded)
@@ -4305,13 +5301,19 @@ class TestAny(CommonMixin, TestCase):
             list(obj.pps())
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             list(obj.pps())
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
+            tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
+            self.assertEqual(obj.tag_order, (tag_class, tag_num))
             obj_encoded = obj.encode()
             obj_encoded = obj.encode()
+            self.assertEqual(encode2pass(obj), obj_encoded)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
             obj_expled = obj(value, expl=tag_expl)
             self.assertTrue(obj_expled.expled)
+            tag_class, _, tag_num = tag_decode(tag_expl)
+            self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
             repr(obj_expled)
             list(obj_expled.pps())
             pprint(obj_expled, big_blobs=True, with_decode_path=True)
             obj_expled_encoded = obj_expled.encode()
+            ctx_dummy["keep_memoryview"] = keep_memoryview
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
             ctx_copied = deepcopy(ctx_dummy)
             obj_decoded, tail = obj_expled.decode(
                 obj_expled_encoded + tail_junk,
@@ -4326,6 +5328,10 @@ class TestAny(CommonMixin, TestCase):
             self.assertEqual(obj_decoded, obj_expled)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
             self.assertEqual(obj_decoded, obj_expled)
             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
             self.assertEqual(bytes(obj_decoded), bytes(obj))
+            self.assertIsInstance(
+                obj_decoded._value,
+                memoryview if keep_memoryview else bytes,
+            )
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
@@ -4349,6 +5355,20 @@ class TestAny(CommonMixin, TestCase):
                 tail_junk,
             )
 
                 tail_junk,
             )
 
+            evgens = list(obj_expled.decode_evgen(
+                obj_expled_encoded + tail_junk,
+                offset=offset,
+                decode_path=decode_path,
+                ctx=ctx_copied,
+            ))
+            self.assertEqual(len(evgens), 1)
+            _decode_path, obj, tail = evgens[0]
+            self.assertSequenceEqual(tail, tail_junk)
+            self.assertEqual(_decode_path, decode_path)
+            self.assertEqual(obj.expl_offset, offset)
+            repr(obj)
+            list(obj.pps())
+
     @given(
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0, max_value=3),
     @given(
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0, max_value=3),
@@ -4489,11 +5509,11 @@ class TestChoice(CommonMixin, TestCase):
     base_klass = Wahl
 
     def test_schema_required(self):
     base_klass = Wahl
 
     def test_schema_required(self):
-        with assertRaisesRegex(self, ValueError, "schema must be specified"):
+        with self.assertRaisesRegex(ValueError, "schema must be specified"):
             Choice()
 
     def test_impl_forbidden(self):
             Choice()
 
     def test_impl_forbidden(self):
-        with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
+        with self.assertRaisesRegex(ValueError, "no implicit tag allowed"):
             Choice(impl=b"whatever")
 
     def test_invalid_value_type(self):
             Choice(impl=b"whatever")
 
     def test_invalid_value_type(self):
@@ -4526,6 +5546,8 @@ class TestChoice(CommonMixin, TestCase):
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             obj.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(obj)
         obj["whatever"] = Boolean()
         self.assertFalse(obj.ready)
         repr(obj)
         obj["whatever"] = Boolean()
         self.assertFalse(obj.ready)
         repr(obj)
@@ -4658,6 +5680,7 @@ class TestChoice(CommonMixin, TestCase):
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         tail_junk = d.draw(binary(max_size=5))
         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
         offset = d.draw(integers(min_value=0))
         tail_junk = d.draw(binary(max_size=5))
+        decode_path = d.draw(decode_path_strat)
 
         class Wahl(self.base_klass):
             schema = _schema
 
         class Wahl(self.base_klass):
             schema = _schema
@@ -4671,9 +5694,13 @@ class TestChoice(CommonMixin, TestCase):
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         list(obj.pps())
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
+        self.assertEqual(obj.tag_order, obj.value.tag_order)
         obj_encoded = obj.encode()
         obj_encoded = obj.encode()
+        self.assertEqual(encode2pass(obj), obj_encoded)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
+        tag_class, _, tag_num = tag_decode(tag_expl)
+        self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
         repr(obj_expled)
         list(obj_expled.pps())
         pprint(obj_expled, big_blobs=True, with_decode_path=True)
         repr(obj_expled)
         list(obj_expled.pps())
         pprint(obj_expled, big_blobs=True, with_decode_path=True)
@@ -4721,6 +5748,22 @@ class TestChoice(CommonMixin, TestCase):
             tail_junk,
         )
 
             tail_junk,
         )
 
+        evgens = list(obj_expled.decode_evgen(
+            obj_expled_encoded + tail_junk,
+            offset=offset,
+            decode_path=decode_path,
+            ctx=ctx_copied,
+        ))
+        self.assertEqual(len(evgens), 2)
+        _decode_path, obj, tail = evgens[0]
+        self.assertEqual(_decode_path, decode_path + (obj_decoded.choice,))
+        _decode_path, obj, tail = evgens[1]
+        self.assertSequenceEqual(tail, tail_junk)
+        self.assertEqual(_decode_path, decode_path)
+        self.assertEqual(obj.expl_offset, offset)
+        repr(obj)
+        list(obj.pps())
+
     @given(integers())
     def test_set_get(self, value):
         class Wahl(Choice):
     @given(integers())
     def test_set_get(self, value):
         class Wahl(Choice):
@@ -4941,7 +5984,7 @@ def sequences_strategy(draw, seq_klass):
     return seq_outer, expect_outers
 
 
     return seq_outer, expect_outers
 
 
-class SeqMixing(object):
+class SeqMixin(object):
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
             self.base_klass(123)
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
             self.base_klass(123)
@@ -5001,6 +6044,8 @@ class SeqMixing(object):
         with self.assertRaises(ObjNotReady) as err:
             seq.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             seq.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(seq)
         for name, value in non_ready.items():
             seq[name] = Boolean(value)
         self.assertTrue(seq.ready)
         for name, value in non_ready.items():
             seq[name] = Boolean(value)
         self.assertTrue(seq.ready)
@@ -5104,8 +6149,9 @@ class SeqMixing(object):
         with self.assertRaises(NotEnoughData):
             seq.decode(seq.encode()[:-1])
 
         with self.assertRaises(NotEnoughData):
             seq.decode(seq.encode()[:-1])
 
-    @given(binary(min_size=2))
-    def test_non_tag_mismatch_raised(self, junk):
+    @given(integers(min_value=3), binary(min_size=2))
+    def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
+        junk = tag_encode(junk_tag_num) + junk
         try:
             _, _, len_encoded = tag_strip(memoryview(junk))
             len_decode(len_encoded)
         try:
             _, _, len_encoded = tag_strip(memoryview(junk))
             len_decode(len_encoded)
@@ -5179,6 +6225,7 @@ class SeqMixing(object):
     def test_symmetric(self, d):
         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
         tail_junk = d.draw(binary(max_size=5))
     def test_symmetric(self, d):
         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
         tail_junk = d.draw(binary(max_size=5))
+        decode_path = d.draw(decode_path_strat)
         self.assertTrue(seq.ready)
         self.assertFalse(seq.decoded)
         self._assert_expects(seq, expects)
         self.assertTrue(seq.ready)
         self.assertFalse(seq.decoded)
         self._assert_expects(seq, expects)
@@ -5187,6 +6234,13 @@ class SeqMixing(object):
         pprint(seq, big_blobs=True, with_decode_path=True)
         self.assertTrue(seq.ready)
         seq_encoded = seq.encode()
         pprint(seq, big_blobs=True, with_decode_path=True)
         self.assertTrue(seq.ready)
         seq_encoded = seq.encode()
+        self.assertEqual(encode2pass(seq), seq_encoded)
+        seq_encoded_cer = encode_cer(seq)
+        self.assertNotEqual(seq_encoded_cer, seq_encoded)
+        self.assertSequenceEqual(
+            seq.decod(seq_encoded_cer, ctx={"bered": True}).encode(),
+            seq_encoded,
+        )
         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
         self.assertFalse(seq_decoded.lenindef)
         self.assertFalse(seq_decoded.ber_encoded)
         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
         self.assertFalse(seq_decoded.lenindef)
         self.assertFalse(seq_decoded.ber_encoded)
@@ -5195,6 +6249,8 @@ class SeqMixing(object):
         t, _, lv = tag_strip(seq_encoded)
         _, _, v = len_decode(lv)
         seq_encoded_lenindef = t + LENINDEF + v + EOC
         t, _, lv = tag_strip(seq_encoded)
         _, _, v = len_decode(lv)
         seq_encoded_lenindef = t + LENINDEF + v + EOC
+        with self.assertRaises(DecodeError):
+            seq.decode(seq_encoded_lenindef)
         ctx_copied = deepcopy(ctx_dummy)
         ctx_copied["bered"] = True
         seq_decoded_lenindef, tail_lenindef = seq.decode(
         ctx_copied = deepcopy(ctx_dummy)
         ctx_copied["bered"] = True
         seq_decoded_lenindef, tail_lenindef = seq.decode(
@@ -5240,6 +6296,21 @@ class SeqMixing(object):
                     obj.encode(),
                 )
 
                     obj.encode(),
                 )
 
+            evgens = list(seq.decode_evgen(
+                encoded + decoded_tail,
+                decode_path=decode_path,
+                ctx={"bered": True},
+            ))
+            self.assertEqual(len(evgens), len(list(decoded._values_for_encoding())) + 1)
+            for _decode_path, obj, _ in evgens[:-1]:
+                self.assertEqual(_decode_path[:-1], decode_path)
+                repr(obj)
+                list(obj.pps())
+            _decode_path, obj, tail = evgens[-1]
+            self.assertEqual(_decode_path, decode_path)
+            repr(obj)
+            list(obj.pps())
+
         assert_exceeding_data(
             self,
             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
         assert_exceeding_data(
             self,
             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
@@ -5252,6 +6323,7 @@ class SeqMixing(object):
         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
         self.assertTrue(seq.ready)
         seq_encoded = seq.encode()
         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
         self.assertTrue(seq.ready)
         seq_encoded = seq.encode()
+        self.assertEqual(encode2pass(seq), seq_encoded)
         seq_decoded, tail = seq.decode(seq_encoded)
         self.assertEqual(tail, b"")
         self.assertTrue(seq.ready)
         seq_decoded, tail = seq.decode(seq_encoded)
         self.assertEqual(tail, b"")
         self.assertTrue(seq.ready)
@@ -5304,29 +6376,39 @@ class SeqMixing(object):
             min_size=1,
         )).items())
         tags = [tag_encode(tag) for tag in d.draw(sets(
             min_size=1,
         )).items())
         tags = [tag_encode(tag) for tag in d.draw(sets(
-            integers(min_value=0),
+            integers(min_value=1),
             min_size=len(_schema),
             max_size=len(_schema),
         ))]
 
             min_size=len(_schema),
             max_size=len(_schema),
         ))]
 
+        class Wahl(Choice):
+            schema = (("int", Integer()),)
+
         class SeqWithoutDefault(self.base_klass):
             schema = [
         class SeqWithoutDefault(self.base_klass):
             schema = [
-                (n, Integer(impl=t))
+                (n, Wahl(expl=t))
                 for (n, _), t in zip(_schema, tags)
             ]
         seq_without_default = SeqWithoutDefault()
         for name, value in _schema:
                 for (n, _), t in zip(_schema, tags)
             ]
         seq_without_default = SeqWithoutDefault()
         for name, value in _schema:
-            seq_without_default[name] = Integer(value)
+            seq_without_default[name] = Wahl(("int", Integer(value)))
         seq_encoded = seq_without_default.encode()
         seq_encoded = seq_without_default.encode()
+        seq_without_default.decode(seq_encoded)
+        self.assertEqual(
+            len(list(seq_without_default.decode_evgen(seq_encoded))),
+            len(_schema) * 2 + 1,
+        )
 
         class SeqWithDefault(self.base_klass):
             schema = [
 
         class SeqWithDefault(self.base_klass):
             schema = [
-                (n, Integer(default=v, impl=t))
+                (n, Wahl(default=Wahl(("int", Integer(v))), expl=t))
                 for (n, v), t in zip(_schema, tags)
             ]
         seq_with_default = SeqWithDefault()
                 for (n, v), t in zip(_schema, tags)
             ]
         seq_with_default = SeqWithDefault()
-        with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+        with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
             seq_with_default.decode(seq_encoded)
             seq_with_default.decode(seq_encoded)
+        with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
+            list(seq_with_default.decode_evgen(seq_encoded))
         for ctx in ({"bered": True}, {"allow_default_values": True}):
             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
             self.assertTrue(seq_decoded.ber_encoded)
         for ctx in ({"bered": True}, {"allow_default_values": True}):
             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
             self.assertTrue(seq_decoded.ber_encoded)
@@ -5336,13 +6418,27 @@ class SeqMixing(object):
             self.assertTrue(seq_decoded.bered)
             for name, value in _schema:
                 self.assertEqual(seq_decoded[name], seq_with_default[name])
             self.assertTrue(seq_decoded.bered)
             for name, value in _schema:
                 self.assertEqual(seq_decoded[name], seq_with_default[name])
-                self.assertEqual(seq_decoded[name], value)
+                self.assertEqual(seq_decoded[name].value, value)
+            self.assertEqual(
+                len(list(seq_with_default.decode_evgen(seq_encoded, ctx=ctx))),
+                len(_schema) + 1,
+            )
+
+        seq_without_default = SeqWithoutDefault()
+        for name, value in _schema:
+            seq_without_default[name] = Wahl(("int", Integer(value + 1)))
+        seq_encoded = seq_without_default.encode()
+        seq_with_default.decode(seq_encoded)
+        self.assertEqual(
+            len(list(seq_with_default.decode_evgen(seq_encoded))),
+            len(_schema) + 1,
+        )
 
     @given(data_strategy())
     def test_missing_from_spec(self, d):
         names = list(d.draw(sets(text_letters(), min_size=2)))
         tags = [tag_encode(tag) for tag in d.draw(sets(
 
     @given(data_strategy())
     def test_missing_from_spec(self, d):
         names = list(d.draw(sets(text_letters(), min_size=2)))
         tags = [tag_encode(tag) for tag in d.draw(sets(
-            integers(min_value=0),
+            integers(min_value=1),
             min_size=len(names),
             max_size=len(names),
         ))]
             min_size=len(names),
             max_size=len(names),
         ))]
@@ -5361,6 +6457,8 @@ class SeqMixing(object):
         seq_missing = SeqMissing()
         with self.assertRaises(TagMismatch):
             seq_missing.decode(seq_encoded)
         seq_missing = SeqMissing()
         with self.assertRaises(TagMismatch):
             seq_missing.decode(seq_encoded)
+        with self.assertRaises(TagMismatch):
+            list(seq_missing.decode_evgen(seq_encoded))
 
     def test_bered(self):
         class Seq(self.base_klass):
 
     def test_bered(self):
         class Seq(self.base_klass):
@@ -5387,6 +6485,9 @@ class SeqMixing(object):
         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
         with self.assertRaises(DecodeError):
             Seq().decode(encoded)
         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
         with self.assertRaises(DecodeError):
             Seq().decode(encoded)
+        with self.assertRaises(DecodeError):
+            list(Seq().decode_evgen(encoded))
+        list(Seq().decode_evgen(encoded, ctx={"bered": True}))
         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
         self.assertFalse(decoded.ber_encoded)
         self.assertFalse(decoded.lenindef)
         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
         self.assertFalse(decoded.ber_encoded)
         self.assertFalse(decoded.lenindef)
@@ -5397,7 +6498,7 @@ class SeqMixing(object):
         self.assertTrue(decoded.bered)
 
 
         self.assertTrue(decoded.bered)
 
 
-class TestSequence(SeqMixing, CommonMixin, TestCase):
+class TestSequence(SeqMixin, CommonMixin, TestCase):
     base_klass = Sequence
 
     @given(
     base_klass = Sequence
 
     @given(
@@ -5415,7 +6516,7 @@ class TestSequence(SeqMixing, CommonMixin, TestCase):
             len_encode(len(int_encoded + junk)),
             int_encoded + junk,
         ))
             len_encode(len(int_encoded + junk)),
             int_encoded + junk,
         ))
-        with assertRaisesRegex(self, DecodeError, "remaining"):
+        with self.assertRaisesRegex(DecodeError, "remaining"):
             Seq().decode(junked)
 
     @given(sets(text_letters(), min_size=2))
             Seq().decode(junked)
 
     @given(sets(text_letters(), min_size=2))
@@ -5443,49 +6544,51 @@ class TestSequence(SeqMixing, CommonMixin, TestCase):
         self.assertEqual(seq["ok"], True)
 
 
         self.assertEqual(seq["ok"], True)
 
 
-class TestSet(SeqMixing, CommonMixin, TestCase):
+class TestSet(SeqMixin, CommonMixin, TestCase):
     base_klass = Set
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_sorted(self, d):
     base_klass = Set
 
     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(data_strategy())
     def test_sorted(self, d):
-        tags = [
-            tag_encode(tag) for tag in
-            d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
-        ]
+        class DummySeq(Sequence):
+            schema = (("null", Null()),)
+
+        tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
+        _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
+        assume(any(i > dummy_seq_tag_num for i in tag_nums))
+        tag_nums -= set([dummy_seq_tag_num])
+        _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
+        _schema.append(("seq", DummySeq()))
 
         class Seq(Set):
 
         class Seq(Set):
-            schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
+            schema = d.draw(permutations(_schema))
         seq = Seq()
         seq = Seq()
-        for name, _ in Seq.schema:
-            seq[name] = OctetString(b"")
+        for name, _ in _schema:
+            if name != "seq":
+                seq[name] = OctetString(name.encode("ascii"))
+        seq["seq"] = DummySeq((("null", Null()),))
+
         seq_encoded = seq.encode()
         seq_decoded, _ = seq.decode(seq_encoded)
         seq_encoded = seq.encode()
         seq_decoded, _ = seq.decode(seq_encoded)
+        seq_encoded_expected = []
+        for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
+            if tag_num == dummy_seq_tag_num:
+                seq_encoded_expected.append(seq["seq"].encode())
+            else:
+                seq_encoded_expected.append(seq[str(tag_num)].encode())
         self.assertSequenceEqual(
             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
         self.assertSequenceEqual(
             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
-            b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
+            b"".join(seq_encoded_expected),
         )
 
         )
 
-    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
-    @given(data_strategy())
-    def test_unsorted(self, d):
-        tags = [
-            tag_encode(tag) for tag in
-            d.draw(sets(integers(min_value=1), min_size=2, max_size=5))
-        ]
-        tags = d.draw(permutations(tags))
-        assume(tags != sorted(tags))
-        encoded = b"".join(OctetString(t, impl=t).encode() for t in tags)
+        encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
+        encoded += seq["seq"].encode()
         seq_encoded = b"".join((
             Set.tag_default,
             len_encode(len(encoded)),
             encoded,
         ))
         seq_encoded = b"".join((
             Set.tag_default,
             len_encode(len(encoded)),
             encoded,
         ))
-
-        class Seq(Set):
-            schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
-        seq = Seq()
-        with assertRaisesRegex(self, DecodeError, "unordered SET"):
+        with self.assertRaisesRegex(DecodeError, "unordered SET"):
             seq.decode(seq_encoded)
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
             seq.decode(seq_encoded)
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
@@ -5494,11 +6597,23 @@ class TestSet(SeqMixing, CommonMixin, TestCase):
             seq_decoded = copy(seq_decoded)
             self.assertTrue(seq_decoded.ber_encoded)
             self.assertTrue(seq_decoded.bered)
             seq_decoded = copy(seq_decoded)
             self.assertTrue(seq_decoded.ber_encoded)
             self.assertTrue(seq_decoded.bered)
-            self.assertSequenceEqual(
-                [bytes(seq_decoded[str(i)]) for i, t in enumerate(tags)],
-                tags,
+
+    def test_same_value_twice(self):
+        class Seq(Set):
+            schema = (
+                ("bool", Boolean()),
+                ("int", Integer()),
             )
 
             )
 
+        encoded = b"".join((
+            Integer(123).encode(),
+            Integer(234).encode(),
+            Boolean(True).encode(),
+        ))
+        encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
+        with self.assertRaises(TagMismatch):
+            Seq().decod(encoded, ctx={"allow_unordered_set": True})
+
 
 @composite
 def seqof_values_strategy(draw, schema=None, do_expl=False):
 
 @composite
 def seqof_values_strategy(draw, schema=None, do_expl=False):
@@ -5544,7 +6659,7 @@ def seqof_values_strategy(draw, schema=None, do_expl=False):
     )
 
 
     )
 
 
-class SeqOfMixing(object):
+class SeqOfMixin(object):
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
             self.base_klass(123)
     def test_invalid_value_type(self):
         with self.assertRaises(InvalidValueType) as err:
             self.base_klass(123)
@@ -5558,10 +6673,10 @@ class SeqOfMixing(object):
         repr(err.exception)
 
     def test_schema_required(self):
         repr(err.exception)
 
     def test_schema_required(self):
-        with assertRaisesRegex(self, ValueError, "schema must be specified"):
+        with self.assertRaisesRegex(ValueError, "schema must be specified"):
             self.base_klass.__mro__[1]()
 
             self.base_klass.__mro__[1]()
 
-    @given(booleans(), booleans(), binary(), binary())
+    @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
     def test_comparison(self, value1, value2, tag1, tag2):
         class SeqOf(self.base_klass):
             schema = Boolean()
     def test_comparison(self, value1, value2, tag1, tag2):
         class SeqOf(self.base_klass):
             schema = Boolean()
@@ -5610,6 +6725,8 @@ class SeqOfMixing(object):
         with self.assertRaises(ObjNotReady) as err:
             seqof.encode()
         repr(err.exception)
         with self.assertRaises(ObjNotReady) as err:
             seqof.encode()
         repr(err.exception)
+        with self.assertRaises(ObjNotReady) as err:
+            encode2pass(seqof)
         for i, value in enumerate(values):
             self.assertEqual(seqof[i], value)
             if not seqof[i].ready:
         for i, value in enumerate(values):
             self.assertEqual(seqof[i], value)
             if not seqof[i].ready:
@@ -5644,15 +6761,19 @@ class SeqOfMixing(object):
             schema = Boolean()
         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
             schema = Boolean()
         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
-        value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
+        value = [Boolean(False)] * d.draw(integers(min_value=0, max_value=bound_min - 1))
         with self.assertRaises(BoundsError) as err:
             SeqOf(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
         with self.assertRaises(BoundsError) as err:
             SeqOf(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 SeqOf(value).encode()
             )
         repr(err.exception)
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 SeqOf(value).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            SeqOf(bounds=(bound_min, bound_max)).decode(
+                encode2pass(SeqOf(value))
+            )
         value = [Boolean(True)] * d.draw(integers(
             min_value=bound_max + 1,
             max_value=bound_max + 10,
         value = [Boolean(True)] * d.draw(integers(
             min_value=bound_max + 1,
             max_value=bound_max + 10,
@@ -5660,11 +6781,15 @@ class SeqOfMixing(object):
         with self.assertRaises(BoundsError) as err:
             SeqOf(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
         with self.assertRaises(BoundsError) as err:
             SeqOf(value=value, bounds=(bound_min, bound_max))
         repr(err.exception)
-        with assertRaisesRegex(self, DecodeError, "bounds") as err:
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 SeqOf(value).encode()
             )
         repr(err.exception)
             SeqOf(bounds=(bound_min, bound_max)).decode(
                 SeqOf(value).encode()
             )
         repr(err.exception)
+        with self.assertRaisesRegex(DecodeError, "bounds") as err:
+            SeqOf(bounds=(bound_min, bound_max)).decode(
+                encode2pass(SeqOf(value))
+            )
 
     @given(integers(min_value=1, max_value=10))
     def test_out_of_bounds(self, bound_max):
 
     @given(integers(min_value=1, max_value=10))
     def test_out_of_bounds(self, bound_max):
@@ -5853,8 +6978,9 @@ class SeqOfMixing(object):
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
         integers(min_value=1).map(tag_ctxc),
         integers(min_value=0),
         binary(max_size=5),
+        decode_path_strat,
     )
     )
-    def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
+    def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
         _, _, _, _, _, default, optional, _decoded = values
 
         class SeqOf(self.base_klass):
         _, _, _, _, _, default, optional, _decoded = values
 
         class SeqOf(self.base_klass):
@@ -5870,6 +6996,13 @@ class SeqOfMixing(object):
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
+        self.assertEqual(encode2pass(obj), obj_encoded)
+        obj_encoded_cer = encode_cer(obj)
+        self.assertNotEqual(obj_encoded_cer, obj_encoded)
+        self.assertSequenceEqual(
+            obj.decod(obj_encoded_cer, ctx={"bered": True}).encode(),
+            obj_encoded,
+        )
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         repr(obj_expled)
         obj_expled = obj(value, expl=tag_expl)
         self.assertTrue(obj_expled.expled)
         repr(obj_expled)
@@ -5915,6 +7048,8 @@ class SeqOfMixing(object):
         t, _, lv = tag_strip(obj_encoded)
         _, _, v = len_decode(lv)
         obj_encoded_lenindef = t + LENINDEF + v + EOC
         t, _, lv = tag_strip(obj_encoded)
         _, _, v = len_decode(lv)
         obj_encoded_lenindef = t + LENINDEF + v + EOC
+        with self.assertRaises(DecodeError):
+            obj.decode(obj_encoded_lenindef)
         obj_decoded_lenindef, tail_lenindef = obj.decode(
             obj_encoded_lenindef + tail_junk,
             ctx={"bered": True},
         obj_decoded_lenindef, tail_lenindef = obj.decode(
             obj_encoded_lenindef + tail_junk,
             ctx={"bered": True},
@@ -5934,6 +7069,21 @@ class SeqOfMixing(object):
         with self.assertRaises(DecodeError):
             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
 
         with self.assertRaises(DecodeError):
             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
 
+        evgens = list(obj.decode_evgen(
+            obj_encoded_lenindef + tail_junk,
+            decode_path=decode_path,
+            ctx={"bered": True},
+        ))
+        self.assertEqual(len(evgens), len(obj_decoded_lenindef) + 1)
+        for i, (_decode_path, obj, _) in enumerate(evgens[:-1]):
+            self.assertEqual(_decode_path, decode_path + (str(i),))
+            repr(obj)
+            list(obj.pps())
+        _decode_path, obj, tail = evgens[-1]
+        self.assertEqual(_decode_path, decode_path)
+        repr(obj)
+        list(obj.pps())
+
         assert_exceeding_data(
             self,
             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
         assert_exceeding_data(
             self,
             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
@@ -5979,7 +7129,7 @@ class SeqOfMixing(object):
         self.assertTrue(decoded.bered)
 
 
         self.assertTrue(decoded.bered)
 
 
-class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
+class TestSequenceOf(SeqOfMixin, CommonMixin, TestCase):
     class SeqOf(SequenceOf):
         schema = "whatever"
     base_klass = SeqOf
     class SeqOf(SequenceOf):
         schema = "whatever"
     base_klass = SeqOf
@@ -5988,8 +7138,82 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
         self.assertEqual(obj1, obj2)
         self.assertSequenceEqual(list(obj1), list(obj2))
 
         self.assertEqual(obj1, obj2)
         self.assertSequenceEqual(list(obj1), list(obj2))
 
+    def test_iterator_pickling(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+        register_class(SeqOf)
+        seqof = SeqOf()
+        pickle_dumps(seqof)
+        seqof = seqof(iter(range(10)))
+        with self.assertRaisesRegex(ValueError, "iterator"):
+            pickle_dumps(seqof)
 
 
-class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
+    def test_iterator_bounds(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+            bounds = (10, 20)
+        seqof = None
+
+        def gen(n):
+            for i in range(n):
+                yield Integer(i)
+        for n in (9, 21):
+            seqof = SeqOf(gen(n))
+            self.assertTrue(seqof.ready)
+            with self.assertRaises(BoundsError):
+                seqof.encode()
+            self.assertFalse(seqof.ready)
+            seqof = seqof(gen(n))
+            self.assertTrue(seqof.ready)
+            with self.assertRaises(BoundsError):
+                encode_cer(seqof)
+            self.assertFalse(seqof.ready)
+
+    def test_iterator_twice(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+            bounds = (1, float("+inf"))
+
+        def gen():
+            for i in range(10):
+                yield Integer(i)
+        seqof = SeqOf(gen())
+        self.assertTrue(seqof.ready)
+        seqof.encode()
+        self.assertFalse(seqof.ready)
+        register_class(SeqOf)
+        pickle_dumps(seqof)
+
+    def test_iterator_2pass(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+            bounds = (1, float("+inf"))
+
+        def gen():
+            for i in range(10):
+                yield Integer(i)
+        seqof = SeqOf(gen())
+        self.assertTrue(seqof.ready)
+        _, state = seqof.encode1st()
+        self.assertFalse(seqof.ready)
+        seqof = seqof(gen())
+        self.assertTrue(seqof.ready)
+        buf = BytesIO()
+        seqof.encode2nd(buf.write, iter(state))
+        self.assertSequenceEqual(
+            [int(i) for i in seqof.decod(buf.getvalue())],
+            list(gen()),
+        )
+
+    def test_non_ready_bound_min(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+            bounds = (1, float("+inf"))
+        seqof = SeqOf()
+        self.assertFalse(seqof.ready)
+
+
+class TestSetOf(SeqOfMixin, CommonMixin, TestCase):
     class SeqOf(SetOf):
         schema = "whatever"
     base_klass = SeqOf
     class SeqOf(SetOf):
         schema = "whatever"
     base_klass = SeqOf
@@ -6035,7 +7259,7 @@ class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
         class Seq(SetOf):
             schema = OctetString()
         seq = Seq()
         class Seq(SetOf):
             schema = OctetString()
         seq = Seq()
-        with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
+        with self.assertRaisesRegex(DecodeError, "unordered SET OF"):
             seq.decode(seq_encoded)
 
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
             seq.decode(seq_encoded)
 
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
@@ -6193,10 +7417,10 @@ class TestGoMarshalVectors(TestCase):
         seq["erste"] = PrintableString("test")
         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
         # Asterisk is actually not allowable
         seq["erste"] = PrintableString("test")
         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
         # Asterisk is actually not allowable
-        PrintableString._allowable_chars |= set(b"*")
+        pyderasn.PRINTABLE_ALLOWABLE_CHARS |= set(b"*")
         seq["erste"] = PrintableString("test*")
         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
         seq["erste"] = PrintableString("test*")
         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
-        PrintableString._allowable_chars -= set(b"*")
+        pyderasn.PRINTABLE_ALLOWABLE_CHARS -= set(b"*")
 
         class Seq(Sequence):
             schema = (
 
         class Seq(Sequence):
             schema = (
@@ -6242,7 +7466,11 @@ class TestPP(TestCase):
     def test_oid_printing(self, d):
         oids = {
             str(ObjectIdentifier(k)): v * 2
     def test_oid_printing(self, d):
         oids = {
             str(ObjectIdentifier(k)): v * 2
-            for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
+            for k, v in d.draw(dictionaries(
+                oid_strategy(),
+                text_letters(),
+                min_size=1,
+            )).items()
         }
         chosen = d.draw(sampled_from(sorted(oids)))
         chosen_id = oids[chosen]
         }
         chosen = d.draw(sampled_from(sorted(oids)))
         chosen_id = oids[chosen]
@@ -6279,32 +7507,33 @@ class TestOIDDefines(TestCase):
             min_size=len(value_names),
             max_size=len(value_names),
         ))
             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))))
+        for definable_class in (Any, OctetString, BitString):
+            _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, definable_class(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)
-        repr(seq)
-        list(seq.pps())
-        pprint(seq, big_blobs=True, with_decode_path=True)
+            class Seq(Sequence):
+                schema = _schema
+            seq = Seq()
+            for value_name, value in zip(value_names, values):
+                seq[value_name] = definable_class(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)
+            repr(seq)
+            list(seq.pps())
+            pprint(seq, big_blobs=True, with_decode_path=True)
 
 
 class TestDefinesByPath(TestCase):
 
 
 class TestDefinesByPath(TestCase):
@@ -6355,10 +7584,10 @@ class TestDefinesByPath(TestCase):
             (type_integered, Integer(234)),
         )
         for t, v in pairs_input:
             (type_integered, Integer(234)),
         )
         for t, v in pairs_input:
-            pair = Pair()
-            pair["type"] = t
-            pair["value"] = PairValue((Any(v),))
-            pairs.append(pair)
+            pairs.append(Pair((
+                ("type", t),
+                ("value", PairValue((Any(v),))),
+            )))
         seq_inner = SeqInner()
         seq_inner["typeInner"] = type_innered
         seq_inner["valueInner"] = Any(pairs)
         seq_inner = SeqInner()
         seq_inner["typeInner"] = type_innered
         seq_inner["valueInner"] = Any(pairs)
@@ -6496,6 +7725,45 @@ class TestDefinesByPath(TestCase):
         decoded, _ = Outer().decode(outer.encode())
         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
 
         decoded, _ = Outer().decode(outer.encode())
         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
 
+    def test_remaining_data(self):
+        oid = ObjectIdentifier("1.2.3")
+
+        class Seq(Sequence):
+            schema = (
+                ("oid", ObjectIdentifier(defines=((("tgt",), {
+                    oid: Integer(),
+                }),))),
+                ("tgt", OctetString()),
+            )
+
+        seq = Seq((
+            ("oid", oid),
+            ("tgt", OctetString(Integer(123).encode() + b"junk")),
+        ))
+        with self.assertRaisesRegex(DecodeError, "remaining data"):
+            Seq().decode(seq.encode())
+
+    def test_remaining_data_seqof(self):
+        oid = ObjectIdentifier("1.2.3")
+
+        class SeqOf(SetOf):
+            schema = OctetString()
+
+        class Seq(Sequence):
+            schema = (
+                ("oid", ObjectIdentifier(defines=((("tgt",), {
+                    oid: Integer(),
+                }),))),
+                ("tgt", SeqOf()),
+            )
+
+        seq = Seq((
+            ("oid", oid),
+            ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
+        ))
+        with self.assertRaisesRegex(DecodeError, "remaining data"):
+            Seq().decode(seq.encode())
+
 
 class TestAbsDecodePath(TestCase):
     @given(
 
 class TestAbsDecodePath(TestCase):
     @given(
@@ -6503,10 +7771,9 @@ class TestAbsDecodePath(TestCase):
         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
     )
     def test_concat(self, decode_path, rel_path):
         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,
-        )
+        dp = abs_decode_path(decode_path, rel_path)
+        self.assertSequenceEqual(dp, decode_path + rel_path)
+        repr(dp)
 
     @given(
         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
 
     @given(
         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
@@ -6548,7 +7815,7 @@ class TestStrictDefaultExistence(TestCase):
             raw = seq.encode()
             chosen_choice = "int%d" % chosen
             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
             raw = seq.encode()
             chosen_choice = "int%d" % chosen
             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
-            with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
+            with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
                 seq.decode(raw)
             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
             self.assertTrue(decoded.ber_encoded)
                 seq.decode(raw)
             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
             self.assertTrue(decoded.ber_encoded)
@@ -6565,11 +7832,13 @@ class TestStrictDefaultExistence(TestCase):
 
 
 class TestX690PrefixedType(TestCase):
 
 
 class TestX690PrefixedType(TestCase):
-    def runTest(self):
+    def test_1(self):
         self.assertSequenceEqual(
             VisibleString("Jones").encode(),
             hexdec("1A054A6F6E6573"),
         )
         self.assertSequenceEqual(
             VisibleString("Jones").encode(),
             hexdec("1A054A6F6E6573"),
         )
+
+    def test_2(self):
         self.assertSequenceEqual(
             VisibleString(
                 "Jones",
         self.assertSequenceEqual(
             VisibleString(
                 "Jones",
@@ -6577,6 +7846,8 @@ class TestX690PrefixedType(TestCase):
             ).encode(),
             hexdec("43054A6F6E6573"),
         )
             ).encode(),
             hexdec("43054A6F6E6573"),
         )
+
+    def test_3(self):
         self.assertSequenceEqual(
             Any(
                 VisibleString(
         self.assertSequenceEqual(
             Any(
                 VisibleString(
@@ -6587,6 +7858,8 @@ class TestX690PrefixedType(TestCase):
             ).encode(),
             hexdec("A20743054A6F6E6573"),
         )
             ).encode(),
             hexdec("A20743054A6F6E6573"),
         )
+
+    def test_4(self):
         self.assertSequenceEqual(
             OctetString(
                 VisibleString(
         self.assertSequenceEqual(
             OctetString(
                 VisibleString(
@@ -6597,6 +7870,8 @@ class TestX690PrefixedType(TestCase):
             ).encode(),
             hexdec("670743054A6F6E6573"),
         )
             ).encode(),
             hexdec("670743054A6F6E6573"),
         )
+
+    def test_5(self):
         self.assertSequenceEqual(
             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
             hexdec("82054A6F6E6573"),
         self.assertSequenceEqual(
             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
             hexdec("82054A6F6E6573"),
@@ -6608,7 +7883,7 @@ class TestExplOOB(TestCase):
         expl = tag_ctxc(123)
         raw = Integer(123).encode() + Integer(234).encode()
         raw = b"".join((expl, len_encode(len(raw)), raw))
         expl = tag_ctxc(123)
         raw = Integer(123).encode() + Integer(234).encode()
         raw = b"".join((expl, len_encode(len(raw)), raw))
-        with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
+        with self.assertRaisesRegex(DecodeError, "explicit tag out-of-bound"):
             Integer(expl=expl).decode(raw)
         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
 
             Integer(expl=expl).decode(raw)
         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
 
@@ -6619,7 +7894,53 @@ class TestPickleDifferentVersion(TestCase):
         import pyderasn
         version_orig = pyderasn.__version__
         pyderasn.__version__ += "different"
         import pyderasn
         version_orig = pyderasn.__version__
         pyderasn.__version__ += "different"
-        with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
+        with self.assertRaisesRegex(ValueError, "different PyDERASN version"):
             pickle_loads(pickled)
         pyderasn.__version__ = version_orig
         pickle_loads(pickled)
             pickle_loads(pickled)
         pyderasn.__version__ = version_orig
         pickle_loads(pickled)
+
+
+class TestCERSetOrdering(TestCase):
+    def test_vectors(self):
+        """Taken from X.690-201508
+        """
+        class B(Choice):
+            schema = (
+                ("c", Integer(impl=tag_ctxp(2))),
+                ("d", Integer(impl=tag_ctxp(4))),
+            )
+
+        class F(Choice):
+            schema = (
+                ("g", Integer(impl=tag_ctxp(5))),
+                ("h", Integer(impl=tag_ctxp(6))),
+            )
+
+        class I(Choice):
+            schema = (
+                ("j", Integer(impl=tag_ctxp(0))),
+            )
+
+        class E(Choice):
+            schema = (
+                ("f", F()),
+                ("i", I()),
+            )
+
+        class A(Set):
+            schema = (
+                ("a", Integer(impl=tag_ctxp(3))),
+                ("b", B(expl=tag_ctxc(1))),
+                ("e", E()),
+            )
+
+        a = A((
+            ("a", Integer(123)),
+            ("b", B(("d", Integer(234)))),
+            ("e", E(("f", F(("g", Integer(345)))))),
+        ))
+        order = sorted(a._values_for_encoding(), key=attrgetter("tag_order_cer"))
+        self.assertSequenceEqual(
+            [i.__class__.__name__ for i in order],
+            ("E", "B", "Integer"),
+        )