]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - tests/test_pyderasn.py
Split TestX690PrefixedType test cases
[pyderasn.git] / tests / test_pyderasn.py
index 29a3cb2da95a79a7a1f5d861d2fbc3c4bcac1697..0fa0485ec9680cfe1261bb24dcafe6375d7bd3af 100644 (file)
 from copy import copy
 from copy import deepcopy
 from datetime import datetime
+from datetime import timedelta
 from importlib import import_module
+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 time import mktime
 from time import time
 from unittest import TestCase
 
@@ -54,6 +60,7 @@ from six import iterbytes
 from six import PY2
 from six import text_type
 from six import unichr as six_unichr
+from six.moves import xrange as six_xrange
 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
@@ -68,6 +75,7 @@ from pyderasn import BoundsError
 from pyderasn import Choice
 from pyderasn import DecodeError
 from pyderasn import DecodePathDefBy
+from pyderasn import encode_cer
 from pyderasn import Enumerated
 from pyderasn import EOC
 from pyderasn import EOC_LEN
@@ -123,8 +131,10 @@ from pyderasn import VideotexString
 from pyderasn import VisibleString
 
 
+max_examples = environ.get("MAX_EXAMPLES")
 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
@@ -155,9 +165,11 @@ def register_class(klass):
 
 
 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 assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)) as err:
+        call()
+    repr(err)
 
 
 class TestHex(TestCase):
@@ -337,14 +349,20 @@ class CommonMixin(object):
         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)
+        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)
@@ -355,6 +373,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)
+        if obj.ready:
+            self.assertEqual(obj.tag_order, obj_copied.tag_order)
 
 
 @composite
@@ -409,7 +429,7 @@ class TestBoolean(CommonMixin, TestCase):
         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)
@@ -567,8 +587,9 @@ class TestBoolean(CommonMixin, TestCase):
         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(
@@ -582,11 +603,18 @@ class TestBoolean(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            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_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)
             obj_decoded, tail = obj_expled.hexdecode(
@@ -623,6 +651,21 @@ class TestBoolean(CommonMixin, TestCase):
                 tail_junk,
             )
 
+            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))
     def test_invalid_len(self, l):
         with self.assertRaises(InvalidLength):
@@ -640,14 +683,13 @@ class TestBoolean(CommonMixin, TestCase):
                 len_encode(1),
                 int2byte(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),
+            int2byte(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)
@@ -709,6 +751,7 @@ class TestBoolean(CommonMixin, TestCase):
         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(
@@ -828,7 +871,7 @@ class TestInteger(CommonMixin, TestCase):
         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)
@@ -1065,8 +1108,9 @@ class TestInteger(CommonMixin, TestCase):
         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(
@@ -1080,12 +1124,19 @@ class TestInteger(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            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_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,
@@ -1121,6 +1172,21 @@ class TestInteger(CommonMixin, TestCase):
                 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),
@@ -1459,6 +1525,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))
+        decode_path = d.draw(decode_path_strat)
         for klass in (BitString, BitStringInherited):
             class BS(klass):
                 schema = _schema
@@ -1473,12 +1540,19 @@ class TestBitString(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            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_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,
@@ -1518,6 +1592,20 @@ class TestBitString(CommonMixin, TestCase):
                 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):
@@ -1564,6 +1652,7 @@ class TestBitString(CommonMixin, TestCase):
         self.assertTrue(obj[9])
         self.assertFalse(obj[17])
 
+    @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
     @given(
         integers(min_value=1, max_value=30),
         lists(
@@ -1580,8 +1669,9 @@ class TestBitString(CommonMixin, TestCase):
         ),
         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) +
@@ -1590,6 +1680,7 @@ class TestBitString(CommonMixin, TestCase):
                 EOC
             )
         chunks = []
+        chunks_len_expected = []
         payload_expected = b""
         bit_len_expected = 0
         for chunk_input in chunk_inputs:
@@ -1597,14 +1688,19 @@ class TestBitString(CommonMixin, TestCase):
                 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
+                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
         ))
+        chunks_len_expected.append(BitString().decod(chunk_last.encode()).vlen)
         payload_expected += bytes(chunk_last)
         bit_len_expected += chunk_last.bit_len
         encoded_indefinite = (
@@ -1641,6 +1737,19 @@ class TestBitString(CommonMixin, TestCase):
             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),
@@ -1777,6 +1886,24 @@ class TestBitString(CommonMixin, TestCase):
         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):
@@ -2053,8 +2180,9 @@ class TestOctetString(CommonMixin, TestCase):
         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 (OctetString, OctetStringInherited):
             _, _, _, _, default, optional, _decoded = values
             obj = klass(
@@ -2068,12 +2196,19 @@ class TestOctetString(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            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_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,
@@ -2109,6 +2244,21 @@ class TestOctetString(CommonMixin, TestCase):
                 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(
@@ -2124,8 +2274,9 @@ class TestOctetString(CommonMixin, TestCase):
             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) +
@@ -2134,15 +2285,20 @@ class TestOctetString(CommonMixin, TestCase):
                 EOC
             )
         chunks = []
+        chunks_len_expected = []
         payload_expected = b""
         for chunk_input in chunk_inputs:
             if isinstance(chunk_input, binary_type):
                 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
+                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 +
@@ -2174,6 +2330,19 @@ class TestOctetString(CommonMixin, TestCase):
             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),
@@ -2230,6 +2399,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))
 
+    @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):
@@ -2262,7 +2449,7 @@ class TestNull(CommonMixin, TestCase):
         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)
@@ -2368,8 +2555,9 @@ class TestNull(CommonMixin, TestCase):
         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)
@@ -2378,12 +2566,19 @@ class TestNull(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            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_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,
@@ -2417,6 +2612,22 @@ class TestNull(CommonMixin, TestCase):
                 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):
@@ -2492,7 +2703,7 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         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)
@@ -2705,8 +2916,9 @@ class TestObjectIdentifier(CommonMixin, TestCase):
         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(
@@ -2720,12 +2932,19 @@ class TestObjectIdentifier(CommonMixin, TestCase):
             pprint(obj, big_blobs=True, with_decode_path=True)
             self.assertFalse(obj.expled)
             obj_encoded = obj.encode()
+            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_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,
@@ -2761,6 +2980,21 @@ class TestObjectIdentifier(CommonMixin, TestCase):
                 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),
@@ -2962,7 +3196,7 @@ class TestEnumerated(CommonMixin, TestCase):
         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 = (
@@ -3078,6 +3312,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))
+        decode_path = d.draw(decode_path_strat)
 
         class E(Enumerated):
             schema = schema_input
@@ -3133,6 +3368,21 @@ class TestEnumerated(CommonMixin, TestCase):
             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):
@@ -3414,6 +3664,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))
+        decode_path = d.draw(decode_path_strat)
         _, _, _, _, default, optional, _decoded = values
         obj = self.base_klass(
             value=value,
@@ -3469,6 +3720,22 @@ class StringMixin(object):
             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)
+        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())
+
 
 class TestUTF8String(StringMixin, CommonMixin, TestCase):
     base_klass = UTF8String
@@ -3942,11 +4209,16 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
     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 test_repr_not_ready(self):
+        unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
+        repr(GeneralizedTime())
+
     def test_x690_vector_valid(self):
         for data in ((
                 b"19920521000000Z",
@@ -3999,6 +4271,239 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
             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):
+        min_year = 1901 if PY2 else 2
+        year = d.draw(integers(min_value=min_year, 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()),
+            )
+        elif not PY2:
+            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,
@@ -4046,16 +4551,58 @@ class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
             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)
+
 
 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 test_repr_not_ready(self):
+        unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
+        repr(UTCTime())
+
     def test_x690_vector_valid(self):
         for data in ((
                 b"920521000000Z",
@@ -4116,6 +4663,229 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
             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(
@@ -4152,9 +4922,16 @@ class TestUTCTime(TimeMixin, CommonMixin, TestCase):
             )
 
 
+@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):
-    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)))
@@ -4184,7 +4961,7 @@ class TestAny(CommonMixin, TestCase):
         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)
@@ -4218,7 +4995,7 @@ class TestAny(CommonMixin, TestCase):
             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)
@@ -4282,7 +5059,7 @@ class TestAny(CommonMixin, TestCase):
             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):
@@ -4329,8 +5106,9 @@ class TestAny(CommonMixin, TestCase):
         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 (Any, AnyInherited):
             _, _, optional, _decoded = values
             obj = klass(value=value, optional=optional, _decoded=_decoded)
@@ -4338,9 +5116,13 @@ class TestAny(CommonMixin, TestCase):
             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_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)
@@ -4382,6 +5164,20 @@ class TestAny(CommonMixin, TestCase):
                 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),
@@ -4691,6 +5487,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))
+        decode_path = d.draw(decode_path_strat)
 
         class Wahl(self.base_klass):
             schema = _schema
@@ -4704,9 +5501,12 @@ class TestChoice(CommonMixin, TestCase):
         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_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)
@@ -4754,6 +5554,22 @@ class TestChoice(CommonMixin, TestCase):
             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):
@@ -5137,8 +5953,9 @@ class SeqMixing(object):
         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)
@@ -5212,6 +6029,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))
+        decode_path = d.draw(decode_path_strat)
         self.assertTrue(seq.ready)
         self.assertFalse(seq.decoded)
         self._assert_expects(seq, expects)
@@ -5220,6 +6038,12 @@ class SeqMixing(object):
         pprint(seq, big_blobs=True, with_decode_path=True)
         self.assertTrue(seq.ready)
         seq_encoded = seq.encode()
+        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)
@@ -5228,6 +6052,8 @@ class SeqMixing(object):
         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(
@@ -5273,6 +6099,21 @@ class SeqMixing(object):
                     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}),
@@ -5337,7 +6178,7 @@ class SeqMixing(object):
             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),
         ))]
@@ -5375,7 +6216,7 @@ class SeqMixing(object):
     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),
         ))]
@@ -5482,42 +6323,44 @@ class TestSet(SeqMixing, CommonMixin, TestCase):
     @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):
-            schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
+            schema = d.draw(permutations(_schema))
         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_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:],
-            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,
         ))
-
-        class Seq(Set):
-            schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
-        seq = Seq()
         with assertRaisesRegex(self, DecodeError, "unordered SET"):
             seq.decode(seq_encoded)
         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
@@ -5527,11 +6370,23 @@ class TestSet(SeqMixing, CommonMixin, TestCase):
             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):
@@ -5594,7 +6449,7 @@ class SeqOfMixing(object):
         with assertRaisesRegex(self, ValueError, "schema must be specified"):
             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()
@@ -5886,8 +6741,9 @@ class SeqOfMixing(object):
         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):
@@ -5903,6 +6759,12 @@ class SeqOfMixing(object):
         pprint(obj, big_blobs=True, with_decode_path=True)
         self.assertFalse(obj.expled)
         obj_encoded = obj.encode()
+        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)
@@ -5948,6 +6810,8 @@ class SeqOfMixing(object):
         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},
@@ -5967,6 +6831,21 @@ class SeqOfMixing(object):
         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),
@@ -6021,6 +6900,57 @@ class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
         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(six_xrange(10)))
+        with assertRaisesRegex(self, ValueError, "iterator"):
+            pickle_dumps(seqof)
+
+    def test_iterator_bounds(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+            bounds = (10, 20)
+        seqof = None
+        def gen(n):
+            for i in six_xrange(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 six_xrange(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_non_ready_bound_min(self):
+        class SeqOf(SequenceOf):
+            schema = Integer()
+            bounds = (1, float("+inf"))
+        seqof = SeqOf()
+        self.assertFalse(seqof.ready)
+
 
 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
     class SeqOf(SetOf):
@@ -6312,32 +7242,33 @@ class TestOIDDefines(TestCase):
             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):
@@ -6388,10 +7319,10 @@ class TestDefinesByPath(TestCase):
             (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)
@@ -6529,6 +7460,43 @@ class TestDefinesByPath(TestCase):
         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 assertRaisesRegex(self, 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 assertRaisesRegex(self, DecodeError, "remaining data"):
+            Seq().decode(seq.encode())
+
 
 class TestAbsDecodePath(TestCase):
     @given(
@@ -6536,10 +7504,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):
-        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),
@@ -6598,11 +7565,13 @@ class TestStrictDefaultExistence(TestCase):
 
 
 class TestX690PrefixedType(TestCase):
-    def runTest(self):
+    def test_1(self):
         self.assertSequenceEqual(
             VisibleString("Jones").encode(),
             hexdec("1A054A6F6E6573"),
         )
+
+    def test_2(self):
         self.assertSequenceEqual(
             VisibleString(
                 "Jones",
@@ -6610,6 +7579,8 @@ class TestX690PrefixedType(TestCase):
             ).encode(),
             hexdec("43054A6F6E6573"),
         )
+
+    def test_3(self):
         self.assertSequenceEqual(
             Any(
                 VisibleString(
@@ -6620,6 +7591,8 @@ class TestX690PrefixedType(TestCase):
             ).encode(),
             hexdec("A20743054A6F6E6573"),
         )
+
+    def test_4(self):
         self.assertSequenceEqual(
             OctetString(
                 VisibleString(
@@ -6630,6 +7603,8 @@ class TestX690PrefixedType(TestCase):
             ).encode(),
             hexdec("670743054A6F6E6573"),
         )
+
+    def test_5(self):
         self.assertSequenceEqual(
             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
             hexdec("82054A6F6E6573"),
@@ -6656,3 +7631,49 @@ class TestPickleDifferentVersion(TestCase):
             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"),
+        )