]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Split TestX690PrefixedType test cases
[pyderasn.git] / tests / test_pyderasn.py
1 # coding: utf-8
2 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
3 # Copyright (C) 2017-2020 Sergey Matveev <stargrave@stargrave.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, version 3 of the License.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this program.  If not, see
16 # <http://www.gnu.org/licenses/>.
17
18 from copy import copy
19 from copy import deepcopy
20 from datetime import datetime
21 from datetime import timedelta
22 from importlib import import_module
23 from operator import attrgetter
24 from os import environ
25 from os import urandom
26 from random import random
27 from string import ascii_letters
28 from string import digits
29 from string import printable
30 from string import whitespace
31 from time import mktime
32 from time import time
33 from unittest import TestCase
34
35 from hypothesis import assume
36 from hypothesis import given
37 from hypothesis import settings
38 from hypothesis.strategies import binary
39 from hypothesis.strategies import booleans
40 from hypothesis.strategies import composite
41 from hypothesis.strategies import data as data_strategy
42 from hypothesis.strategies import datetimes
43 from hypothesis.strategies import dictionaries
44 from hypothesis.strategies import integers
45 from hypothesis.strategies import just
46 from hypothesis.strategies import lists
47 from hypothesis.strategies import none
48 from hypothesis.strategies import one_of
49 from hypothesis.strategies import permutations
50 from hypothesis.strategies import sampled_from
51 from hypothesis.strategies import sets
52 from hypothesis.strategies import text
53 from hypothesis.strategies import tuples
54 from six import assertRaisesRegex
55 from six import binary_type
56 from six import byte2int
57 from six import indexbytes
58 from six import int2byte
59 from six import iterbytes
60 from six import PY2
61 from six import text_type
62 from six import unichr as six_unichr
63 from six.moves import xrange as six_xrange
64 from six.moves.cPickle import dumps as pickle_dumps
65 from six.moves.cPickle import HIGHEST_PROTOCOL as pickle_proto
66 from six.moves.cPickle import loads as pickle_loads
67
68 from pyderasn import _pp
69 from pyderasn import abs_decode_path
70 from pyderasn import Any
71 from pyderasn import BitString
72 from pyderasn import BMPString
73 from pyderasn import Boolean
74 from pyderasn import BoundsError
75 from pyderasn import Choice
76 from pyderasn import DecodeError
77 from pyderasn import DecodePathDefBy
78 from pyderasn import encode_cer
79 from pyderasn import Enumerated
80 from pyderasn import EOC
81 from pyderasn import EOC_LEN
82 from pyderasn import ExceedingData
83 from pyderasn import GeneralizedTime
84 from pyderasn import GeneralString
85 from pyderasn import GraphicString
86 from pyderasn import hexdec
87 from pyderasn import hexenc
88 from pyderasn import IA5String
89 from pyderasn import Integer
90 from pyderasn import InvalidLength
91 from pyderasn import InvalidOID
92 from pyderasn import InvalidValueType
93 from pyderasn import len_decode
94 from pyderasn import len_encode
95 from pyderasn import LEN_YYMMDDHHMMSSZ
96 from pyderasn import LEN_YYYYMMDDHHMMSSDMZ
97 from pyderasn import LEN_YYYYMMDDHHMMSSZ
98 from pyderasn import LENINDEF
99 from pyderasn import LenIndefForm
100 from pyderasn import NotEnoughData
101 from pyderasn import Null
102 from pyderasn import NumericString
103 from pyderasn import ObjectIdentifier
104 from pyderasn import ObjNotReady
105 from pyderasn import ObjUnknown
106 from pyderasn import OctetString
107 from pyderasn import pp_console_row
108 from pyderasn import pprint
109 from pyderasn import PrintableString
110 from pyderasn import Sequence
111 from pyderasn import SequenceOf
112 from pyderasn import Set
113 from pyderasn import SetOf
114 from pyderasn import tag_ctxc
115 from pyderasn import tag_ctxp
116 from pyderasn import tag_decode
117 from pyderasn import tag_encode
118 from pyderasn import tag_strip
119 from pyderasn import TagClassApplication
120 from pyderasn import TagClassContext
121 from pyderasn import TagClassPrivate
122 from pyderasn import TagClassUniversal
123 from pyderasn import TagFormConstructed
124 from pyderasn import TagFormPrimitive
125 from pyderasn import TagMismatch
126 from pyderasn import TeletexString
127 from pyderasn import UniversalString
128 from pyderasn import UTCTime
129 from pyderasn import UTF8String
130 from pyderasn import VideotexString
131 from pyderasn import VisibleString
132
133
134 max_examples = environ.get("MAX_EXAMPLES")
135 settings.register_profile("local", settings(
136     deadline=5000,
137     **({"max_examples": int(max_examples)} if max_examples else {})
138 ))
139 settings.load_profile("local")
140 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
141
142 tag_classes = sampled_from((
143     TagClassApplication,
144     TagClassContext,
145     TagClassPrivate,
146     TagClassUniversal,
147 ))
148 tag_forms = sampled_from((TagFormConstructed, TagFormPrimitive))
149 decode_path_strat = lists(integers(), max_size=3).map(
150     lambda decode_path: tuple(str(dp) for dp in decode_path)
151 )
152 ctx_dummy = dictionaries(integers(), integers(), min_size=2, max_size=4).example()
153 copy_funcs = (
154     copy,
155     lambda obj: pickle_loads(pickle_dumps(obj, pickle_proto)),
156 )
157 self_module = import_module(__name__)
158
159
160 def register_class(klass):
161     klassname = klass.__name__ + str(time()).replace(".", "")
162     klass.__name__ = klassname
163     klass.__qualname__ = klassname
164     setattr(self_module, klassname, klass)
165
166
167 def assert_exceeding_data(self, call, junk):
168     if len(junk) <= 0:
169         return
170     with assertRaisesRegex(self, ExceedingData, "%d trailing bytes" % len(junk)) as err:
171         call()
172     repr(err)
173
174
175 class TestHex(TestCase):
176     @given(binary())
177     def test_symmetric(self, data):
178         self.assertEqual(hexdec(hexenc(data)), data)
179
180
181 class TestTagCoder(TestCase):
182     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
183     @given(
184         tag_classes,
185         tag_forms,
186         integers(min_value=0, max_value=30),
187         binary(max_size=5),
188     )
189     def test_short(self, klass, form, num, junk):
190         raw = tag_encode(klass=klass, form=form, num=num)
191         self.assertEqual(tag_decode(raw), (klass, form, num))
192         self.assertEqual(len(raw), 1)
193         self.assertEqual(
194             byte2int(tag_encode(klass=klass, form=form, num=0)),
195             byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
196         )
197         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
198         self.assertSequenceEqual(stripped.tobytes(), raw)
199         self.assertEqual(tlen, len(raw))
200         self.assertSequenceEqual(tail, junk)
201
202     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
203     @given(
204         tag_classes,
205         tag_forms,
206         integers(min_value=31),
207         binary(max_size=5),
208     )
209     def test_long(self, klass, form, num, junk):
210         raw = tag_encode(klass=klass, form=form, num=num)
211         self.assertEqual(tag_decode(raw), (klass, form, num))
212         self.assertGreater(len(raw), 1)
213         self.assertEqual(
214             byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
215             byte2int(raw[:1]),
216         )
217         self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
218         self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
219         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
220         self.assertSequenceEqual(stripped.tobytes(), raw)
221         self.assertEqual(tlen, len(raw))
222         self.assertSequenceEqual(tail, junk)
223
224     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
225     @given(integers(min_value=31))
226     def test_unfinished_tag(self, num):
227         raw = bytearray(tag_encode(num=num))
228         for i in range(1, len(raw)):
229             raw[i] |= 0x80
230         with assertRaisesRegex(self, DecodeError, "unfinished tag"):
231             tag_strip(bytes(raw))
232
233     def test_go_vectors_valid(self):
234         for data, (eklass, etag, elen, eform) in (
235                 (b"\x80\x01", (TagClassContext, 0, 1, TagFormPrimitive)),
236                 (b"\xa0\x01", (TagClassContext, 0, 1, TagFormConstructed)),
237                 (b"\x02\x00", (TagClassUniversal, 2, 0, TagFormPrimitive)),
238                 (b"\xfe\x00", (TagClassPrivate, 30, 0, TagFormConstructed)),
239                 (b"\x1f\x1f\x00", (TagClassUniversal, 31, 0, TagFormPrimitive)),
240                 (b"\x1f\x81\x00\x00", (TagClassUniversal, 128, 0, TagFormPrimitive)),
241                 (b"\x1f\x81\x80\x01\x00", (TagClassUniversal, 0x4001, 0, TagFormPrimitive)),
242                 (b"\x00\x81\x80", (TagClassUniversal, 0, 128, TagFormPrimitive)),
243                 (b"\x00\x82\x01\x00", (TagClassUniversal, 0, 256, TagFormPrimitive)),
244                 (b"\xa0\x84\x7f\xff\xff\xff", (TagClassContext, 0, 0x7fffffff, TagFormConstructed)),
245         ):
246             tag, _, len_encoded = tag_strip(memoryview(data))
247             klass, form, num = tag_decode(tag)
248             _len, _, tail = len_decode(len_encoded)
249             self.assertSequenceEqual(tail, b"")
250             self.assertEqual(klass, eklass)
251             self.assertEqual(num, etag)
252             self.assertEqual(_len, elen)
253             self.assertEqual(form, eform)
254
255     def test_go_vectors_invalid(self):
256         for data in (
257                 b"\x00\x83\x01\x00",
258                 b"\x1f\x85",
259                 b"\x30\x80",
260                 b"\xa0\x82\x00\xff",
261                 b"\xa0\x81\x7f",
262         ):
263             with self.assertRaises(DecodeError):
264                 _, _, len_encoded = tag_strip(memoryview(data))
265                 len_decode(len_encoded)
266
267     @given(
268         integers(min_value=0, max_value=127),
269         integers(min_value=0, max_value=2),
270     )
271     def test_long_instead_of_short(self, l, dummy_num):
272         octets = (b"\x00" * dummy_num) + int2byte(l)
273         octets = int2byte((dummy_num + 1) | 0x80) + octets
274         with self.assertRaises(DecodeError):
275             len_decode(octets)
276
277
278 class TestLenCoder(TestCase):
279     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
280     @given(
281         integers(min_value=0, max_value=127),
282         binary(max_size=5),
283     )
284     def test_short(self, l, junk):
285         raw = len_encode(l) + junk
286         decoded, llen, tail = len_decode(memoryview(raw))
287         self.assertEqual(decoded, l)
288         self.assertEqual(llen, 1)
289         self.assertEqual(len(raw), 1 + len(junk))
290         self.assertEqual(tail.tobytes(), junk)
291
292     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
293     @given(
294         integers(min_value=128),
295         binary(max_size=5),
296     )
297     def test_long(self, l, junk):
298         raw = len_encode(l) + junk
299         decoded, llen, tail = len_decode(memoryview(raw))
300         self.assertEqual(decoded, l)
301         self.assertEqual((llen - 1) | 0x80, byte2int(raw))
302         self.assertEqual(llen, len(raw) - len(junk))
303         self.assertNotEqual(indexbytes(raw, 1), 0)
304         self.assertSequenceEqual(tail.tobytes(), junk)
305
306     def test_empty(self):
307         with self.assertRaises(NotEnoughData):
308             len_decode(b"")
309
310     @given(integers(min_value=128))
311     def test_stripped(self, _len):
312         with self.assertRaises(NotEnoughData):
313             len_decode(len_encode(_len)[:-1])
314
315
316 text_printable = text(alphabet=printable, min_size=1)
317
318
319 @composite
320 def text_letters(draw):
321     result = draw(text(alphabet=ascii_letters, min_size=1))
322     if PY2:
323         result = result.encode("ascii")
324     return result
325
326
327 class CommonMixin(object):
328     def test_tag_default(self):
329         obj = self.base_klass()
330         self.assertEqual(obj.tag, obj.tag_default)
331
332     def test_simultaneous_impl_expl(self):
333         with self.assertRaises(ValueError):
334             self.base_klass(impl=b"whatever", expl=b"whenever")
335
336     @given(binary(min_size=1), integers(), integers(), integers())
337     def test_decoded(self, impl, offset, llen, vlen):
338         obj = self.base_klass(impl=impl, _decoded=(offset, llen, vlen))
339         self.assertEqual(obj.offset, offset)
340         self.assertEqual(obj.llen, llen)
341         self.assertEqual(obj.vlen, vlen)
342         self.assertEqual(obj.tlen, len(impl))
343         self.assertEqual(obj.tlvlen, obj.tlen + obj.llen + obj.vlen)
344
345     @given(binary(min_size=1))
346     def test_impl_inherited(self, impl_tag):
347         class Inherited(self.base_klass):
348             impl = impl_tag
349         obj = Inherited()
350         self.assertSequenceEqual(obj.impl, impl_tag)
351         self.assertFalse(obj.expled)
352         if obj.ready:
353             tag_class, _, tag_num = tag_decode(impl_tag)
354             self.assertEqual(obj.tag_order, (tag_class, tag_num))
355
356     @given(binary(min_size=1))
357     def test_expl_inherited(self, expl_tag):
358         class Inherited(self.base_klass):
359             expl = expl_tag
360         obj = Inherited()
361         self.assertSequenceEqual(obj.expl, expl_tag)
362         self.assertTrue(obj.expled)
363         if obj.ready:
364             tag_class, _, tag_num = tag_decode(expl_tag)
365             self.assertEqual(obj.tag_order, (tag_class, tag_num))
366
367     def assert_copied_basic_fields(self, obj, obj_copied):
368         self.assertEqual(obj, obj_copied)
369         self.assertSequenceEqual(obj.tag, obj_copied.tag)
370         self.assertEqual(obj.expl_tag, obj_copied.expl_tag)
371         self.assertEqual(obj.default, obj_copied.default)
372         self.assertEqual(obj.optional, obj_copied.optional)
373         self.assertEqual(obj.offset, obj_copied.offset)
374         self.assertEqual(obj.llen, obj_copied.llen)
375         self.assertEqual(obj.vlen, obj_copied.vlen)
376         if obj.ready:
377             self.assertEqual(obj.tag_order, obj_copied.tag_order)
378
379
380 @composite
381 def boolean_values_strategy(draw, do_expl=False):
382     value = draw(one_of(none(), booleans()))
383     impl = None
384     expl = None
385     if do_expl:
386         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
387     else:
388         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
389     default = draw(one_of(none(), booleans()))
390     optional = draw(one_of(none(), booleans()))
391     _decoded = (
392         draw(integers(min_value=0)),
393         draw(integers(min_value=0)),
394         draw(integers(min_value=0)),
395     )
396     return (value, impl, expl, default, optional, _decoded)
397
398
399 class BooleanInherited(Boolean):
400     pass
401
402
403 class TestBoolean(CommonMixin, TestCase):
404     base_klass = Boolean
405
406     def test_invalid_value_type(self):
407         with self.assertRaises(InvalidValueType) as err:
408             Boolean(123)
409         repr(err.exception)
410
411     @given(booleans())
412     def test_optional(self, optional):
413         obj = Boolean(default=Boolean(False), optional=optional)
414         self.assertTrue(obj.optional)
415
416     @given(booleans())
417     def test_ready(self, value):
418         obj = Boolean()
419         self.assertFalse(obj.ready)
420         repr(obj)
421         list(obj.pps())
422         pprint(obj, big_blobs=True, with_decode_path=True)
423         with self.assertRaises(ObjNotReady) as err:
424             obj.encode()
425         repr(err.exception)
426         obj = Boolean(value)
427         self.assertTrue(obj.ready)
428         repr(obj)
429         list(obj.pps())
430         pprint(obj, big_blobs=True, with_decode_path=True)
431
432     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
433     def test_comparison(self, value1, value2, tag1, tag2):
434         for klass in (Boolean, BooleanInherited):
435             obj1 = klass(value1)
436             obj2 = klass(value2)
437             self.assertEqual(obj1 == obj2, value1 == value2)
438             self.assertEqual(obj1 != obj2, value1 != value2)
439             self.assertEqual(obj1 == bool(obj2), value1 == value2)
440             obj1 = klass(value1, impl=tag1)
441             obj2 = klass(value1, impl=tag2)
442             self.assertEqual(obj1 == obj2, tag1 == tag2)
443             self.assertEqual(obj1 != obj2, tag1 != tag2)
444
445     @given(data_strategy())
446     def test_call(self, d):
447         for klass in (Boolean, BooleanInherited):
448             (
449                 value_initial,
450                 impl_initial,
451                 expl_initial,
452                 default_initial,
453                 optional_initial,
454                 _decoded_initial,
455             ) = d.draw(boolean_values_strategy())
456             obj_initial = klass(
457                 value_initial,
458                 impl_initial,
459                 expl_initial,
460                 default_initial,
461                 optional_initial or False,
462                 _decoded_initial,
463             )
464             (
465                 value,
466                 impl,
467                 expl,
468                 default,
469                 optional,
470                 _decoded,
471             ) = d.draw(boolean_values_strategy(do_expl=impl_initial is None))
472             obj = obj_initial(value, impl, expl, default, optional)
473             if obj.ready:
474                 value_expected = default if value is None else value
475                 value_expected = (
476                     default_initial if value_expected is None
477                     else value_expected
478                 )
479                 self.assertEqual(obj, value_expected)
480             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
481             self.assertEqual(obj.expl_tag, expl or expl_initial)
482             self.assertEqual(
483                 obj.default,
484                 default_initial if default is None else default,
485             )
486             if obj.default is None:
487                 optional = optional_initial if optional is None else optional
488                 optional = False if optional is None else optional
489             else:
490                 optional = True
491             self.assertEqual(obj.optional, optional)
492
493     @given(boolean_values_strategy())
494     def test_copy(self, values):
495         for klass in (Boolean, BooleanInherited):
496             obj = klass(*values)
497             for copy_func in copy_funcs:
498                 obj_copied = copy_func(obj)
499                 self.assert_copied_basic_fields(obj, obj_copied)
500
501     @given(
502         booleans(),
503         integers(min_value=1).map(tag_encode),
504     )
505     def test_stripped(self, value, tag_impl):
506         obj = Boolean(value, impl=tag_impl)
507         with self.assertRaises(NotEnoughData):
508             obj.decode(obj.encode()[:-1])
509
510     @given(
511         booleans(),
512         integers(min_value=1).map(tag_ctxc),
513     )
514     def test_stripped_expl(self, value, tag_expl):
515         obj = Boolean(value, expl=tag_expl)
516         with self.assertRaises(NotEnoughData):
517             obj.decode(obj.encode()[:-1])
518
519     @given(
520         integers(min_value=31),
521         integers(min_value=0),
522         decode_path_strat,
523     )
524     def test_bad_tag(self, tag, offset, decode_path):
525         with self.assertRaises(DecodeError) as err:
526             Boolean().decode(
527                 tag_encode(tag)[:-1],
528                 offset=offset,
529                 decode_path=decode_path,
530             )
531         repr(err.exception)
532         self.assertEqual(err.exception.offset, offset)
533         self.assertEqual(err.exception.decode_path, decode_path)
534
535     @given(
536         integers(min_value=31),
537         integers(min_value=0),
538         decode_path_strat,
539     )
540     def test_bad_expl_tag(self, tag, offset, decode_path):
541         with self.assertRaises(DecodeError) as err:
542             Boolean(expl=Boolean.tag_default).decode(
543                 tag_encode(tag)[:-1],
544                 offset=offset,
545                 decode_path=decode_path,
546             )
547         repr(err.exception)
548         self.assertEqual(err.exception.offset, offset)
549         self.assertEqual(err.exception.decode_path, decode_path)
550
551     @given(
552         integers(min_value=128),
553         integers(min_value=0),
554         decode_path_strat,
555     )
556     def test_bad_len(self, l, offset, decode_path):
557         with self.assertRaises(DecodeError) as err:
558             Boolean().decode(
559                 Boolean.tag_default + len_encode(l)[:-1],
560                 offset=offset,
561                 decode_path=decode_path,
562             )
563         repr(err.exception)
564         self.assertEqual(err.exception.offset, offset)
565         self.assertEqual(err.exception.decode_path, decode_path)
566
567     @given(
568         integers(min_value=128),
569         integers(min_value=0),
570         decode_path_strat,
571     )
572     def test_bad_expl_len(self, l, offset, decode_path):
573         with self.assertRaises(DecodeError) as err:
574             Boolean(expl=Boolean.tag_default).decode(
575                 Boolean.tag_default + len_encode(l)[:-1],
576                 offset=offset,
577                 decode_path=decode_path,
578             )
579         repr(err.exception)
580         self.assertEqual(err.exception.offset, offset)
581         self.assertEqual(err.exception.decode_path, decode_path)
582
583     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
584     @given(
585         boolean_values_strategy(),
586         booleans(),
587         integers(min_value=1).map(tag_ctxc),
588         integers(min_value=0),
589         binary(max_size=5),
590         decode_path_strat,
591     )
592     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
593         for klass in (Boolean, BooleanInherited):
594             _, _, _, default, optional, _decoded = values
595             obj = klass(
596                 value=value,
597                 default=default,
598                 optional=optional,
599                 _decoded=_decoded,
600             )
601             repr(obj)
602             list(obj.pps())
603             pprint(obj, big_blobs=True, with_decode_path=True)
604             self.assertFalse(obj.expled)
605             obj_encoded = obj.encode()
606             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
607             obj_expled = obj(value, expl=tag_expl)
608             self.assertTrue(obj_expled.expled)
609             repr(obj_expled)
610             list(obj_expled.pps())
611             pprint(obj_expled, big_blobs=True, with_decode_path=True)
612             obj_expled_cer = encode_cer(obj_expled)
613             self.assertNotEqual(obj_expled_cer, obj_encoded)
614             self.assertSequenceEqual(
615                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
616                 obj_expled.encode(),
617             )
618             obj_expled_hex_encoded = obj_expled.hexencode()
619             ctx_copied = deepcopy(ctx_dummy)
620             obj_decoded, tail = obj_expled.hexdecode(
621                 obj_expled_hex_encoded + hexenc(tail_junk),
622                 offset=offset,
623                 ctx=ctx_copied,
624             )
625             self.assertDictEqual(ctx_copied, ctx_dummy)
626             repr(obj_decoded)
627             list(obj_decoded.pps())
628             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
629             self.assertEqual(tail, tail_junk)
630             self.assertEqual(obj_decoded, obj_expled)
631             self.assertNotEqual(obj_decoded, obj)
632             self.assertEqual(bool(obj_decoded), bool(obj_expled))
633             self.assertEqual(bool(obj_decoded), bool(obj))
634             self.assertSequenceEqual(obj_decoded.hexencode(), obj_expled_hex_encoded)
635             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
636             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
637             self.assertEqual(
638                 obj_decoded.expl_llen,
639                 len(len_encode(len(obj_encoded))),
640             )
641             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
642             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
643             self.assertEqual(
644                 obj_decoded.offset,
645                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
646             )
647             self.assertEqual(obj_decoded.expl_offset, offset)
648             assert_exceeding_data(
649                 self,
650                 lambda: obj_expled.hexdecod(obj_expled_hex_encoded + hexenc(tail_junk)),
651                 tail_junk,
652             )
653
654             evgens = list(obj_expled.decode_evgen(
655                 hexdec(obj_expled_hex_encoded) + tail_junk,
656                 offset=offset,
657                 decode_path=decode_path,
658                 ctx=ctx_copied,
659             ))
660             self.assertEqual(len(evgens), 1)
661             _decode_path, obj, tail = evgens[0]
662             self.assertSequenceEqual(tail, tail_junk)
663             self.assertEqual(_decode_path, decode_path)
664             self.assertEqual(obj, obj_decoded)
665             self.assertEqual(obj.expl_offset, offset)
666             repr(obj)
667             list(obj.pps())
668
669     @given(integers(min_value=2))
670     def test_invalid_len(self, l):
671         with self.assertRaises(InvalidLength):
672             Boolean().decode(b"".join((
673                 Boolean.tag_default,
674                 len_encode(l),
675                 b"\x00" * l,
676             )))
677
678     @given(integers(min_value=0 + 1, max_value=255 - 1))
679     def test_ber_value(self, value):
680         with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
681             Boolean().decode(b"".join((
682                 Boolean.tag_default,
683                 len_encode(1),
684                 int2byte(value),
685             )))
686         encoded = b"".join((
687             Boolean.tag_default,
688             len_encode(1),
689             int2byte(value),
690         ))
691         obj, _ = Boolean().decode(encoded, ctx={"bered": True})
692         list(Boolean().decode_evgen(encoded, ctx={"bered": True}))
693         self.assertTrue(bool(obj))
694         self.assertTrue(obj.ber_encoded)
695         self.assertFalse(obj.lenindef)
696         self.assertTrue(obj.bered)
697         obj = copy(obj)
698         self.assertTrue(obj.ber_encoded)
699         self.assertFalse(obj.lenindef)
700         self.assertTrue(obj.bered)
701
702     @given(
703         integers(min_value=1).map(tag_ctxc),
704         binary().filter(lambda x: not x.startswith(EOC)),
705     )
706     def test_ber_expl_no_eoc(self, expl, junk):
707         encoded = expl + LENINDEF + Boolean(False).encode()
708         with self.assertRaises(LenIndefForm):
709             Boolean(expl=expl).decode(encoded + junk)
710         with assertRaisesRegex(self, DecodeError, "no EOC"):
711             Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
712         obj, tail = Boolean(expl=expl).decode(
713             encoded + EOC + junk,
714             ctx={"bered": True},
715         )
716         self.assertTrue(obj.expl_lenindef)
717         self.assertFalse(obj.lenindef)
718         self.assertFalse(obj.ber_encoded)
719         self.assertTrue(obj.bered)
720         obj = copy(obj)
721         self.assertTrue(obj.expl_lenindef)
722         self.assertFalse(obj.lenindef)
723         self.assertFalse(obj.ber_encoded)
724         self.assertTrue(obj.bered)
725         self.assertSequenceEqual(tail, junk)
726         repr(obj)
727         list(obj.pps())
728         pprint(obj, big_blobs=True, with_decode_path=True)
729
730     @given(
731         integers(min_value=1).map(tag_ctxc),
732         lists(
733             booleans(),
734             min_size=1,
735             max_size=5,
736         ),
737     )
738     def test_ber_expl(self, expl, values):
739         encoded = b""
740         for value in values:
741             encoded += (
742                 expl +
743                 LENINDEF +
744                 Boolean(value).encode() +
745                 EOC
746             )
747         encoded = SequenceOf.tag_default + len_encode(len(encoded)) + encoded
748
749         class SeqOf(SequenceOf):
750             schema = Boolean(expl=expl)
751         with self.assertRaises(LenIndefForm):
752             SeqOf().decode(encoded)
753         seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
754         list(SeqOf().decode_evgen(encoded, ctx={"bered": True}))
755         self.assertSequenceEqual(tail, b"")
756         self.assertSequenceEqual([bool(v) for v in seqof], values)
757         self.assertSetEqual(
758             set(
759                 (
760                     v.tlvlen,
761                     v.expl_tlvlen,
762                     v.expl_tlen,
763                     v.expl_llen,
764                     v.ber_encoded,
765                     v.lenindef,
766                     v.expl_lenindef,
767                     v.bered,
768                 ) for v in seqof
769             ),
770             set(((
771                 3 + EOC_LEN,
772                 len(expl) + 1 + 3 + EOC_LEN,
773                 len(expl),
774                 1,
775                 False,
776                 False,
777                 True,
778                 True,
779             ),)),
780         )
781         repr(seqof)
782         list(seqof.pps())
783         pprint(seqof, big_blobs=True, with_decode_path=True)
784
785
786 @composite
787 def integer_values_strategy(draw, do_expl=False):
788     bound_min, value, default, bound_max = sorted(draw(sets(
789         integers(),
790         min_size=4,
791         max_size=4,
792     )))
793     if draw(booleans()):
794         value = None
795     _specs = None
796     if draw(booleans()):
797         _specs = draw(sets(text_letters()))
798         values = draw(sets(
799             integers(),
800             min_size=len(_specs),
801             max_size=len(_specs),
802         ))
803         _specs = list(zip(_specs, values))
804     bounds = None
805     if draw(booleans()):
806         bounds = (bound_min, bound_max)
807     impl = None
808     expl = None
809     if do_expl:
810         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
811     else:
812         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
813     if draw(booleans()):
814         default = None
815     optional = draw(one_of(none(), booleans()))
816     _decoded = (
817         draw(integers(min_value=0)),
818         draw(integers(min_value=0)),
819         draw(integers(min_value=0)),
820     )
821     return (value, bounds, impl, expl, default, optional, _specs, _decoded)
822
823
824 class IntegerInherited(Integer):
825     pass
826
827
828 class TestInteger(CommonMixin, TestCase):
829     base_klass = Integer
830
831     def test_invalid_value_type(self):
832         with self.assertRaises(InvalidValueType) as err:
833             Integer(12.3)
834         repr(err.exception)
835
836     @given(sets(text_letters(), min_size=2))
837     def test_unknown_name(self, names_input):
838         missing = names_input.pop()
839
840         class Int(Integer):
841             schema = [(n, 123) for n in names_input]
842         with self.assertRaises(ObjUnknown) as err:
843             Int(missing)
844         repr(err.exception)
845
846     @given(sets(text_letters(), min_size=2))
847     def test_known_name(self, names_input):
848         class Int(Integer):
849             schema = [(n, 123) for n in names_input]
850         Int(names_input.pop())
851
852     @given(booleans())
853     def test_optional(self, optional):
854         obj = Integer(default=Integer(0), optional=optional)
855         self.assertTrue(obj.optional)
856
857     @given(integers())
858     def test_ready(self, value):
859         obj = Integer()
860         self.assertFalse(obj.ready)
861         repr(obj)
862         list(obj.pps())
863         pprint(obj, big_blobs=True, with_decode_path=True)
864         with self.assertRaises(ObjNotReady) as err:
865             obj.encode()
866         repr(err.exception)
867         obj = Integer(value)
868         self.assertTrue(obj.ready)
869         repr(obj)
870         list(obj.pps())
871         pprint(obj, big_blobs=True, with_decode_path=True)
872         hash(obj)
873
874     @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
875     def test_comparison(self, value1, value2, tag1, tag2):
876         for klass in (Integer, IntegerInherited):
877             obj1 = klass(value1)
878             obj2 = klass(value2)
879             self.assertEqual(obj1 == obj2, value1 == value2)
880             self.assertEqual(obj1 != obj2, value1 != value2)
881             self.assertEqual(obj1 == int(obj2), value1 == value2)
882             obj1 = klass(value1, impl=tag1)
883             obj2 = klass(value1, impl=tag2)
884             self.assertEqual(obj1 == obj2, tag1 == tag2)
885             self.assertEqual(obj1 != obj2, tag1 != tag2)
886
887     @given(lists(integers()))
888     def test_sorted_works(self, values):
889         self.assertSequenceEqual(
890             [int(v) for v in sorted(Integer(v) for v in values)],
891             sorted(values),
892         )
893
894     @given(data_strategy())
895     def test_named(self, d):
896         names_input = list(d.draw(sets(text_letters(), min_size=1)))
897         values_input = list(d.draw(sets(
898             integers(),
899             min_size=len(names_input),
900             max_size=len(names_input),
901         )))
902         chosen_name = d.draw(sampled_from(names_input))
903         names_input = dict(zip(names_input, values_input))
904
905         class Int(Integer):
906             schema = names_input
907         _int = Int(chosen_name)
908         self.assertEqual(_int.named, chosen_name)
909         self.assertEqual(int(_int), names_input[chosen_name])
910
911     @given(integers(), integers(min_value=0), integers(min_value=0))
912     def test_bounds_satisfied(self, bound_min, bound_delta, value_delta):
913         value = bound_min + value_delta
914         bound_max = value + bound_delta
915         Integer(value=value, bounds=(bound_min, bound_max))
916
917     @given(sets(integers(), min_size=3, max_size=3))
918     def test_bounds_unsatisfied(self, values):
919         values = sorted(values)
920         with self.assertRaises(BoundsError) as err:
921             Integer(value=values[0], bounds=(values[1], values[2]))
922         repr(err.exception)
923         with assertRaisesRegex(self, DecodeError, "bounds") as err:
924             Integer(bounds=(values[1], values[2])).decode(
925                 Integer(values[0]).encode()
926             )
927         repr(err.exception)
928         with self.assertRaises(BoundsError) as err:
929             Integer(value=values[2], bounds=(values[0], values[1]))
930         repr(err.exception)
931         with assertRaisesRegex(self, DecodeError, "bounds") as err:
932             Integer(bounds=(values[0], values[1])).decode(
933                 Integer(values[2]).encode()
934             )
935         repr(err.exception)
936
937     @given(data_strategy())
938     def test_call(self, d):
939         for klass in (Integer, IntegerInherited):
940             (
941                 value_initial,
942                 bounds_initial,
943                 impl_initial,
944                 expl_initial,
945                 default_initial,
946                 optional_initial,
947                 _specs_initial,
948                 _decoded_initial,
949             ) = d.draw(integer_values_strategy())
950             obj_initial = klass(
951                 value_initial,
952                 bounds_initial,
953                 impl_initial,
954                 expl_initial,
955                 default_initial,
956                 optional_initial or False,
957                 _specs_initial,
958                 _decoded_initial,
959             )
960             (
961                 value,
962                 bounds,
963                 impl,
964                 expl,
965                 default,
966                 optional,
967                 _,
968                 _decoded,
969             ) = d.draw(integer_values_strategy(do_expl=impl_initial is None))
970             if (default is None) and (obj_initial.default is not None):
971                 bounds = None
972             if (
973                     (bounds is None) and
974                     (value is not None) and
975                     (bounds_initial is not None) and
976                     not (bounds_initial[0] <= value <= bounds_initial[1])
977             ):
978                 value = None
979             if (
980                     (bounds is None) and
981                     (default is not None) and
982                     (bounds_initial is not None) and
983                     not (bounds_initial[0] <= default <= bounds_initial[1])
984             ):
985                 default = None
986             obj = obj_initial(value, bounds, impl, expl, default, optional)
987             if obj.ready:
988                 value_expected = default if value is None else value
989                 value_expected = (
990                     default_initial if value_expected is None
991                     else value_expected
992                 )
993                 self.assertEqual(obj, value_expected)
994             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
995             self.assertEqual(obj.expl_tag, expl or expl_initial)
996             self.assertEqual(
997                 obj.default,
998                 default_initial if default is None else default,
999             )
1000             if obj.default is None:
1001                 optional = optional_initial if optional is None else optional
1002                 optional = False if optional is None else optional
1003             else:
1004                 optional = True
1005             self.assertEqual(obj.optional, optional)
1006             self.assertEqual(
1007                 (obj._bound_min, obj._bound_max),
1008                 bounds or bounds_initial or (float("-inf"), float("+inf")),
1009             )
1010             self.assertEqual(
1011                 obj.specs,
1012                 {} if _specs_initial is None else dict(_specs_initial),
1013             )
1014
1015     @given(integer_values_strategy())
1016     def test_copy(self, values):
1017         for klass in (Integer, IntegerInherited):
1018             obj = klass(*values)
1019             for copy_func in copy_funcs:
1020                 obj_copied = copy_func(obj)
1021                 self.assert_copied_basic_fields(obj, obj_copied)
1022                 self.assertEqual(obj.specs, obj_copied.specs)
1023                 self.assertEqual(obj._bound_min, obj_copied._bound_min)
1024                 self.assertEqual(obj._bound_max, obj_copied._bound_max)
1025                 self.assertEqual(obj._value, obj_copied._value)
1026
1027     @given(
1028         integers(),
1029         integers(min_value=1).map(tag_encode),
1030     )
1031     def test_stripped(self, value, tag_impl):
1032         obj = Integer(value, impl=tag_impl)
1033         with self.assertRaises(NotEnoughData):
1034             obj.decode(obj.encode()[:-1])
1035
1036     @given(
1037         integers(),
1038         integers(min_value=1).map(tag_ctxc),
1039     )
1040     def test_stripped_expl(self, value, tag_expl):
1041         obj = Integer(value, expl=tag_expl)
1042         with self.assertRaises(NotEnoughData):
1043             obj.decode(obj.encode()[:-1])
1044
1045     def test_zero_len(self):
1046         with self.assertRaises(NotEnoughData):
1047             Integer().decode(b"".join((
1048                 Integer.tag_default,
1049                 len_encode(0),
1050             )))
1051
1052     @given(
1053         integers(min_value=31),
1054         integers(min_value=0),
1055         decode_path_strat,
1056     )
1057     def test_bad_tag(self, tag, offset, decode_path):
1058         with self.assertRaises(DecodeError) as err:
1059             Integer().decode(
1060                 tag_encode(tag)[:-1],
1061                 offset=offset,
1062                 decode_path=decode_path,
1063             )
1064         repr(err.exception)
1065         self.assertEqual(err.exception.offset, offset)
1066         self.assertEqual(err.exception.decode_path, decode_path)
1067
1068     @given(
1069         integers(min_value=128),
1070         integers(min_value=0),
1071         decode_path_strat,
1072     )
1073     def test_bad_len(self, l, offset, decode_path):
1074         with self.assertRaises(DecodeError) as err:
1075             Integer().decode(
1076                 Integer.tag_default + len_encode(l)[:-1],
1077                 offset=offset,
1078                 decode_path=decode_path,
1079             )
1080         repr(err.exception)
1081         self.assertEqual(err.exception.offset, offset)
1082         self.assertEqual(err.exception.decode_path, decode_path)
1083
1084     @given(
1085         sets(integers(), min_size=2, max_size=2),
1086         integers(min_value=0),
1087         decode_path_strat,
1088     )
1089     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
1090         value, bound_min = list(sorted(ints))
1091
1092         class Int(Integer):
1093             bounds = (bound_min, bound_min)
1094         with self.assertRaises(DecodeError) as err:
1095             Int().decode(
1096                 Integer(value).encode(),
1097                 offset=offset,
1098                 decode_path=decode_path,
1099             )
1100         repr(err.exception)
1101         self.assertEqual(err.exception.offset, offset)
1102         self.assertEqual(err.exception.decode_path, decode_path)
1103
1104     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1105     @given(
1106         integer_values_strategy(),
1107         integers(),
1108         integers(min_value=1).map(tag_ctxc),
1109         integers(min_value=0),
1110         binary(max_size=5),
1111         decode_path_strat,
1112     )
1113     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
1114         for klass in (Integer, IntegerInherited):
1115             _, _, _, _, default, optional, _, _decoded = values
1116             obj = klass(
1117                 value=value,
1118                 default=default,
1119                 optional=optional,
1120                 _decoded=_decoded,
1121             )
1122             repr(obj)
1123             list(obj.pps())
1124             pprint(obj, big_blobs=True, with_decode_path=True)
1125             self.assertFalse(obj.expled)
1126             obj_encoded = obj.encode()
1127             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
1128             obj_expled = obj(value, expl=tag_expl)
1129             self.assertTrue(obj_expled.expled)
1130             repr(obj_expled)
1131             list(obj_expled.pps())
1132             pprint(obj_expled, big_blobs=True, with_decode_path=True)
1133             obj_expled_encoded = obj_expled.encode()
1134             obj_expled_cer = encode_cer(obj_expled)
1135             self.assertNotEqual(obj_expled_cer, obj_encoded)
1136             self.assertSequenceEqual(
1137                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
1138                 obj_expled_encoded,
1139             )
1140             ctx_copied = deepcopy(ctx_dummy)
1141             obj_decoded, tail = obj_expled.decode(
1142                 obj_expled_encoded + tail_junk,
1143                 offset=offset,
1144                 ctx=ctx_copied,
1145             )
1146             self.assertDictEqual(ctx_copied, ctx_dummy)
1147             repr(obj_decoded)
1148             list(obj_decoded.pps())
1149             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
1150             self.assertEqual(tail, tail_junk)
1151             self.assertEqual(obj_decoded, obj_expled)
1152             self.assertNotEqual(obj_decoded, obj)
1153             self.assertEqual(int(obj_decoded), int(obj_expled))
1154             self.assertEqual(int(obj_decoded), int(obj))
1155             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1156             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1157             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1158             self.assertEqual(
1159                 obj_decoded.expl_llen,
1160                 len(len_encode(len(obj_encoded))),
1161             )
1162             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1163             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1164             self.assertEqual(
1165                 obj_decoded.offset,
1166                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1167             )
1168             self.assertEqual(obj_decoded.expl_offset, offset)
1169             assert_exceeding_data(
1170                 self,
1171                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
1172                 tail_junk,
1173             )
1174
1175             evgens = list(obj_expled.decode_evgen(
1176                 obj_expled_encoded + tail_junk,
1177                 offset=offset,
1178                 decode_path=decode_path,
1179                 ctx=ctx_copied,
1180             ))
1181             self.assertEqual(len(evgens), 1)
1182             _decode_path, obj, tail = evgens[0]
1183             self.assertSequenceEqual(tail, tail_junk)
1184             self.assertEqual(_decode_path, decode_path)
1185             self.assertEqual(obj, obj_decoded)
1186             self.assertEqual(obj.expl_offset, offset)
1187             repr(obj)
1188             list(obj.pps())
1189
1190     def test_go_vectors_valid(self):
1191         for data, expect in ((
1192                 (b"\x00", 0),
1193                 (b"\x7f", 127),
1194                 (b"\x80", -128),
1195                 (b"\xff\x7f", -129),
1196                 (b"\xff", -1),
1197                 (b"\x01", 1),
1198                 (b"\x00\xff", 255),
1199                 (b"\xff\x00", -256),
1200                 (b"\x01\x00", 256),
1201                 (b"\x00\x80", 128),
1202                 (b"\x01\x00", 256),
1203                 (b"\x80\x00\x00\x00\x00\x00\x00\x00", -9223372036854775808),
1204                 (b"\x80\x00\x00\x00", -2147483648),
1205         )):
1206             self.assertEqual(
1207                 Integer().decode(b"".join((
1208                     Integer.tag_default,
1209                     len_encode(len(data)),
1210                     data,
1211                 )))[0],
1212                 expect,
1213             )
1214
1215     def test_go_vectors_invalid(self):
1216         for data in ((
1217                 b"\x00\x7f",
1218                 b"\xff\xf0",
1219         )):
1220             with self.assertRaises(DecodeError):
1221                 Integer().decode(b"".join((
1222                     Integer.tag_default,
1223                     len_encode(len(data)),
1224                     data,
1225                 )))
1226
1227
1228 @composite
1229 def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=False):
1230     if schema is None:
1231         schema = ()
1232         if draw(booleans()):
1233             schema = draw(sets(text_letters(), min_size=1, max_size=256))
1234             bits = draw(sets(
1235                 integers(min_value=0, max_value=255),
1236                 min_size=len(schema),
1237                 max_size=len(schema),
1238             ))
1239             schema = list(zip(schema, bits))
1240
1241     def _value(value_required):
1242         if not value_required and draw(booleans()):
1243             return None
1244         generation_choice = 0
1245         if value_required:
1246             generation_choice = draw(sampled_from((1, 2, 3)))
1247         if generation_choice == 1 or draw(booleans()):
1248             return "'%s'B" % "".join(draw(lists(
1249                 sampled_from(("0", "1")),
1250                 max_size=len(schema),
1251             )))
1252         if generation_choice == 2 or draw(booleans()):
1253             return draw(binary(max_size=len(schema) // 8))
1254         if generation_choice == 3 or draw(booleans()):
1255             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
1256         return None
1257     value = _value(value_required)
1258     default = _value(value_required=False)
1259     impl = None
1260     expl = None
1261     if do_expl:
1262         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1263     else:
1264         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1265     optional = draw(one_of(none(), booleans()))
1266     _decoded = (
1267         draw(integers(min_value=0)),
1268         draw(integers(min_value=0)),
1269         draw(integers(min_value=0)),
1270     )
1271     return (schema, value, impl, expl, default, optional, _decoded)
1272
1273
1274 class BitStringInherited(BitString):
1275     pass
1276
1277
1278 class TestBitString(CommonMixin, TestCase):
1279     base_klass = BitString
1280
1281     @given(lists(booleans()))
1282     def test_b_encoding(self, bits):
1283         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1284         self.assertEqual(obj.bit_len, len(bits))
1285         self.assertSequenceEqual(list(obj), bits)
1286         for i, bit in enumerate(bits):
1287             self.assertEqual(obj[i], bit)
1288
1289     @given(lists(booleans()))
1290     def test_out_of_bounds_bits(self, bits):
1291         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1292         for i in range(len(bits), len(bits) * 2):
1293             self.assertFalse(obj[i])
1294
1295     def test_bad_b_encoding(self):
1296         with self.assertRaises(ValueError):
1297             BitString("'010120101'B")
1298
1299     @given(
1300         integers(min_value=1, max_value=255),
1301         integers(min_value=1, max_value=255),
1302     )
1303     def test_named_are_stripped(self, leading_zeros, trailing_zeros):
1304         obj = BitString("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1305         self.assertEqual(obj.bit_len, leading_zeros + 1 + trailing_zeros)
1306         self.assertGreater(len(obj.encode()), (leading_zeros + 1 + trailing_zeros) // 8)
1307
1308         class BS(BitString):
1309             schema = (("whatever", 0),)
1310         obj = BS("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1311         self.assertEqual(obj.bit_len, leading_zeros + 1)
1312         self.assertGreater(len(obj.encode()), (leading_zeros + 1) // 8)
1313
1314     def test_zero_len(self):
1315         with self.assertRaises(NotEnoughData):
1316             BitString().decode(b"".join((
1317                 BitString.tag_default,
1318                 len_encode(0),
1319             )))
1320
1321     def test_invalid_value_type(self):
1322         with self.assertRaises(InvalidValueType) as err:
1323             BitString(123)
1324         repr(err.exception)
1325         with self.assertRaises(InvalidValueType) as err:
1326             BitString(u"123")
1327         repr(err.exception)
1328
1329     def test_obj_unknown(self):
1330         with self.assertRaises(ObjUnknown) as err:
1331             BitString(b"whatever")["whenever"]
1332         repr(err.exception)
1333
1334     def test_get_invalid_type(self):
1335         with self.assertRaises(InvalidValueType) as err:
1336             BitString(b"whatever")[(1, 2, 3)]
1337         repr(err.exception)
1338
1339     @given(data_strategy())
1340     def test_unknown_name(self, d):
1341         _schema = d.draw(sets(text_letters(), min_size=2, max_size=5))
1342         missing = _schema.pop()
1343
1344         class BS(BitString):
1345             schema = [(n, i) for i, n in enumerate(_schema)]
1346         with self.assertRaises(ObjUnknown) as err:
1347             BS((missing,))
1348         repr(err.exception)
1349
1350     @given(booleans())
1351     def test_optional(self, optional):
1352         obj = BitString(default=BitString(b""), optional=optional)
1353         self.assertTrue(obj.optional)
1354
1355     @given(binary())
1356     def test_ready(self, value):
1357         obj = BitString()
1358         self.assertFalse(obj.ready)
1359         repr(obj)
1360         list(obj.pps())
1361         pprint(obj, big_blobs=True, with_decode_path=True)
1362         with self.assertRaises(ObjNotReady) as err:
1363             obj.encode()
1364         repr(err.exception)
1365         obj = BitString(value)
1366         self.assertTrue(obj.ready)
1367         repr(obj)
1368         list(obj.pps())
1369         pprint(obj, big_blobs=True, with_decode_path=True)
1370
1371     @given(
1372         tuples(integers(min_value=0), binary()),
1373         tuples(integers(min_value=0), binary()),
1374         binary(min_size=1),
1375         binary(min_size=1),
1376     )
1377     def test_comparison(self, value1, value2, tag1, tag2):
1378         for klass in (BitString, BitStringInherited):
1379             obj1 = klass(value1)
1380             obj2 = klass(value2)
1381             self.assertEqual(obj1 == obj2, value1 == value2)
1382             self.assertEqual(obj1 != obj2, value1 != value2)
1383             self.assertEqual(obj1 == bytes(obj2), value1[1] == value2[1])
1384             obj1 = klass(value1, impl=tag1)
1385             obj2 = klass(value1, impl=tag2)
1386             self.assertEqual(obj1 == obj2, tag1 == tag2)
1387             self.assertEqual(obj1 != obj2, tag1 != tag2)
1388
1389     @given(data_strategy())
1390     def test_call(self, d):
1391         for klass in (BitString, BitStringInherited):
1392             (
1393                 schema_initial,
1394                 value_initial,
1395                 impl_initial,
1396                 expl_initial,
1397                 default_initial,
1398                 optional_initial,
1399                 _decoded_initial,
1400             ) = d.draw(bit_string_values_strategy())
1401
1402             class BS(klass):
1403                 schema = schema_initial
1404             obj_initial = BS(
1405                 value=value_initial,
1406                 impl=impl_initial,
1407                 expl=expl_initial,
1408                 default=default_initial,
1409                 optional=optional_initial or False,
1410                 _decoded=_decoded_initial,
1411             )
1412             (
1413                 _,
1414                 value,
1415                 impl,
1416                 expl,
1417                 default,
1418                 optional,
1419                 _decoded,
1420             ) = d.draw(bit_string_values_strategy(
1421                 schema=schema_initial,
1422                 do_expl=impl_initial is None,
1423             ))
1424             obj = obj_initial(
1425                 value=value,
1426                 impl=impl,
1427                 expl=expl,
1428                 default=default,
1429                 optional=optional,
1430             )
1431             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1432             self.assertEqual(obj.expl_tag, expl or expl_initial)
1433             if obj.default is None:
1434                 optional = optional_initial if optional is None else optional
1435                 optional = False if optional is None else optional
1436             else:
1437                 optional = True
1438             self.assertEqual(obj.optional, optional)
1439             self.assertEqual(obj.specs, obj_initial.specs)
1440
1441     @given(bit_string_values_strategy())
1442     def test_copy(self, values):
1443         for klass in (BitString, BitStringInherited):
1444             _schema, value, impl, expl, default, optional, _decoded = values
1445
1446             class BS(klass):
1447                 schema = _schema
1448             register_class(BS)
1449             obj = BS(
1450                 value=value,
1451                 impl=impl,
1452                 expl=expl,
1453                 default=default,
1454                 optional=optional or False,
1455                 _decoded=_decoded,
1456             )
1457             for copy_func in copy_funcs:
1458                 obj_copied = copy_func(obj)
1459                 self.assert_copied_basic_fields(obj, obj_copied)
1460                 self.assertEqual(obj.specs, obj_copied.specs)
1461                 self.assertEqual(obj._value, obj_copied._value)
1462
1463     @given(
1464         binary(),
1465         integers(min_value=1).map(tag_encode),
1466     )
1467     def test_stripped(self, value, tag_impl):
1468         obj = BitString(value, impl=tag_impl)
1469         with self.assertRaises(NotEnoughData):
1470             obj.decode(obj.encode()[:-1])
1471
1472     @given(
1473         binary(),
1474         integers(min_value=1).map(tag_ctxc),
1475     )
1476     def test_stripped_expl(self, value, tag_expl):
1477         obj = BitString(value, expl=tag_expl)
1478         with self.assertRaises(NotEnoughData):
1479             obj.decode(obj.encode()[:-1])
1480
1481     @given(
1482         integers(min_value=31),
1483         integers(min_value=0),
1484         decode_path_strat,
1485     )
1486     def test_bad_tag(self, tag, offset, decode_path):
1487         with self.assertRaises(DecodeError) as err:
1488             BitString().decode(
1489                 tag_encode(tag)[:-1],
1490                 offset=offset,
1491                 decode_path=decode_path,
1492             )
1493         repr(err.exception)
1494         self.assertEqual(err.exception.offset, offset)
1495         self.assertEqual(err.exception.decode_path, decode_path)
1496
1497     @given(
1498         integers(min_value=128),
1499         integers(min_value=0),
1500         decode_path_strat,
1501     )
1502     def test_bad_len(self, l, offset, decode_path):
1503         with self.assertRaises(DecodeError) as err:
1504             BitString().decode(
1505                 BitString.tag_default + len_encode(l)[:-1],
1506                 offset=offset,
1507                 decode_path=decode_path,
1508             )
1509         repr(err.exception)
1510         self.assertEqual(err.exception.offset, offset)
1511         self.assertEqual(err.exception.decode_path, decode_path)
1512
1513     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1514     @given(data_strategy())
1515     def test_symmetric(self, d):
1516         (
1517             _schema,
1518             value,
1519             _,
1520             _,
1521             default,
1522             optional,
1523             _decoded,
1524         ) = d.draw(bit_string_values_strategy(value_required=True))
1525         tail_junk = d.draw(binary(max_size=5))
1526         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
1527         offset = d.draw(integers(min_value=0))
1528         decode_path = d.draw(decode_path_strat)
1529         for klass in (BitString, BitStringInherited):
1530             class BS(klass):
1531                 schema = _schema
1532             obj = BS(
1533                 value=value,
1534                 default=default,
1535                 optional=optional,
1536                 _decoded=_decoded,
1537             )
1538             repr(obj)
1539             list(obj.pps())
1540             pprint(obj, big_blobs=True, with_decode_path=True)
1541             self.assertFalse(obj.expled)
1542             obj_encoded = obj.encode()
1543             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
1544             obj_expled = obj(value, expl=tag_expl)
1545             self.assertTrue(obj_expled.expled)
1546             repr(obj_expled)
1547             list(obj_expled.pps())
1548             pprint(obj_expled, big_blobs=True, with_decode_path=True)
1549             obj_expled_encoded = obj_expled.encode()
1550             obj_expled_cer = encode_cer(obj_expled)
1551             self.assertNotEqual(obj_expled_cer, obj_encoded)
1552             self.assertSequenceEqual(
1553                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
1554                 obj_expled_encoded,
1555             )
1556             ctx_copied = deepcopy(ctx_dummy)
1557             obj_decoded, tail = obj_expled.decode(
1558                 obj_expled_encoded + tail_junk,
1559                 offset=offset,
1560                 ctx=ctx_copied,
1561             )
1562             self.assertDictEqual(ctx_copied, ctx_dummy)
1563             repr(obj_decoded)
1564             list(obj_decoded.pps())
1565             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
1566             self.assertEqual(tail, tail_junk)
1567             self.assertEqual(obj_decoded, obj_expled)
1568             self.assertNotEqual(obj_decoded, obj)
1569             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
1570             self.assertEqual(bytes(obj_decoded), bytes(obj))
1571             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1572             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1573             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1574             self.assertEqual(
1575                 obj_decoded.expl_llen,
1576                 len(len_encode(len(obj_encoded))),
1577             )
1578             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1579             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1580             self.assertEqual(
1581                 obj_decoded.offset,
1582                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1583             )
1584             self.assertEqual(obj_decoded.expl_offset, offset)
1585             if isinstance(value, tuple):
1586                 self.assertSetEqual(set(value), set(obj_decoded.named))
1587                 for name in value:
1588                     obj_decoded[name]
1589             assert_exceeding_data(
1590                 self,
1591                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
1592                 tail_junk,
1593             )
1594
1595             evgens = list(obj_expled.decode_evgen(
1596                 obj_expled_encoded + tail_junk,
1597                 offset=offset,
1598                 decode_path=decode_path,
1599                 ctx=ctx_copied,
1600             ))
1601             self.assertEqual(len(evgens), 1)
1602             _decode_path, obj, tail = evgens[0]
1603             self.assertSequenceEqual(tail, tail_junk)
1604             self.assertEqual(_decode_path, decode_path)
1605             self.assertEqual(obj.expl_offset, offset)
1606             repr(obj)
1607             list(obj.pps())
1608
1609     @given(integers(min_value=1, max_value=255))
1610     def test_bad_zero_value(self, pad_size):
1611         with self.assertRaises(DecodeError):
1612             BitString().decode(b"".join((
1613                 BitString.tag_default,
1614                 len_encode(1),
1615                 int2byte(pad_size),
1616             )))
1617
1618     def test_go_vectors_invalid(self):
1619         for data in ((
1620                 b"\x07\x01",
1621                 b"\x07\x40",
1622                 b"\x08\x00",
1623         )):
1624             with self.assertRaises(DecodeError):
1625                 BitString().decode(b"".join((
1626                     BitString.tag_default,
1627                     len_encode(2),
1628                     data,
1629                 )))
1630
1631     def test_go_vectors_valid(self):
1632         obj, _ = BitString().decode(b"".join((
1633             BitString.tag_default,
1634             len_encode(1),
1635             b"\x00",
1636         )))
1637         self.assertEqual(bytes(obj), b"")
1638         self.assertEqual(obj.bit_len, 0)
1639
1640         obj, _ = BitString().decode(b"".join((
1641             BitString.tag_default,
1642             len_encode(2),
1643             b"\x07\x00",
1644         )))
1645         self.assertEqual(bytes(obj), b"\x00")
1646         self.assertEqual(obj.bit_len, 1)
1647
1648         obj = BitString((16, b"\x82\x40"))
1649         self.assertTrue(obj[0])
1650         self.assertFalse(obj[1])
1651         self.assertTrue(obj[6])
1652         self.assertTrue(obj[9])
1653         self.assertFalse(obj[17])
1654
1655     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1656     @given(
1657         integers(min_value=1, max_value=30),
1658         lists(
1659             one_of(
1660                 binary(min_size=1, max_size=5),
1661                 lists(
1662                     binary(min_size=1, max_size=5),
1663                     min_size=1,
1664                     max_size=3,
1665                 ),
1666             ),
1667             min_size=0,
1668             max_size=3,
1669         ),
1670         lists(booleans(), min_size=1),
1671         binary(),
1672         decode_path_strat,
1673     )
1674     def test_constructed(self, impl, chunk_inputs, chunk_last_bits, junk, decode_path):
1675         def chunk_constructed(contents):
1676             return (
1677                 tag_encode(form=TagFormConstructed, num=3) +
1678                 LENINDEF +
1679                 b"".join(BitString(content).encode() for content in contents) +
1680                 EOC
1681             )
1682         chunks = []
1683         chunks_len_expected = []
1684         payload_expected = b""
1685         bit_len_expected = 0
1686         for chunk_input in chunk_inputs:
1687             if isinstance(chunk_input, binary_type):
1688                 chunks.append(BitString(chunk_input).encode())
1689                 payload_expected += chunk_input
1690                 bit_len_expected += len(chunk_input) * 8
1691                 chunks_len_expected.append(len(chunk_input) + 1)
1692             else:
1693                 chunks.append(chunk_constructed(chunk_input))
1694                 payload = b"".join(chunk_input)
1695                 payload_expected += payload
1696                 bit_len_expected += len(payload) * 8
1697                 for c in chunk_input:
1698                     chunks_len_expected.append(len(c) + 1)
1699                 chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
1700         chunk_last = BitString("'%s'B" % "".join(
1701             "1" if bit else "0" for bit in chunk_last_bits
1702         ))
1703         chunks_len_expected.append(BitString().decod(chunk_last.encode()).vlen)
1704         payload_expected += bytes(chunk_last)
1705         bit_len_expected += chunk_last.bit_len
1706         encoded_indefinite = (
1707             tag_encode(form=TagFormConstructed, num=impl) +
1708             LENINDEF +
1709             b"".join(chunks) +
1710             chunk_last.encode() +
1711             EOC
1712         )
1713         encoded_definite = (
1714             tag_encode(form=TagFormConstructed, num=impl) +
1715             len_encode(len(b"".join(chunks) + chunk_last.encode())) +
1716             b"".join(chunks) +
1717             chunk_last.encode()
1718         )
1719         with assertRaisesRegex(self, DecodeError, "unallowed BER"):
1720             BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
1721         for lenindef_expected, encoded in (
1722                 (True, encoded_indefinite),
1723                 (False, encoded_definite),
1724         ):
1725             obj, tail = BitString(impl=tag_encode(impl)).decode(
1726                 encoded + junk,
1727                 ctx={"bered": True},
1728             )
1729             self.assertSequenceEqual(tail, junk)
1730             self.assertEqual(obj.bit_len, bit_len_expected)
1731             self.assertSequenceEqual(bytes(obj), payload_expected)
1732             self.assertTrue(obj.ber_encoded)
1733             self.assertEqual(obj.lenindef, lenindef_expected)
1734             self.assertTrue(obj.bered)
1735             obj = copy(obj)
1736             self.assertTrue(obj.ber_encoded)
1737             self.assertEqual(obj.lenindef, lenindef_expected)
1738             self.assertTrue(obj.bered)
1739             self.assertEqual(len(encoded), obj.tlvlen)
1740             repr(obj)
1741             list(obj.pps())
1742             pprint(obj, big_blobs=True, with_decode_path=True)
1743
1744             evgens = list(BitString(impl=tag_encode(impl)).decode_evgen(
1745                 encoded,
1746                 decode_path=decode_path,
1747                 ctx={"bered": True},
1748             ))
1749             self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
1750             for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
1751                 self.assertGreater(len(dp), len(decode_path))
1752                 self.assertEqual(obj.vlen, chunk_len_expected)
1753
1754     @given(
1755         integers(min_value=0),
1756         decode_path_strat,
1757     )
1758     def test_ber_definite_too_short(self, offset, decode_path):
1759         with assertRaisesRegex(self, DecodeError, "longer than data") as err:
1760             BitString().decode(
1761                 tag_encode(3, form=TagFormConstructed) + len_encode(1),
1762                 offset=offset,
1763                 decode_path=decode_path,
1764                 ctx={"bered": True},
1765             )
1766         self.assertEqual(err.exception.decode_path, decode_path)
1767         self.assertEqual(err.exception.offset, offset)
1768
1769     @given(
1770         integers(min_value=0),
1771         decode_path_strat,
1772     )
1773     def test_ber_definite_no_data(self, offset, decode_path):
1774         with assertRaisesRegex(self, DecodeError, "zero length") as err:
1775             BitString().decode(
1776                 tag_encode(3, form=TagFormConstructed) + len_encode(0),
1777                 offset=offset,
1778                 decode_path=decode_path,
1779                 ctx={"bered": True},
1780             )
1781         self.assertEqual(err.exception.decode_path, decode_path)
1782         self.assertEqual(err.exception.offset, offset)
1783
1784     @given(
1785         integers(min_value=0),
1786         decode_path_strat,
1787         integers(min_value=1, max_value=3),
1788     )
1789     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
1790         bs = BitString(b"data").encode()
1791         with self.assertRaises(NotEnoughData) as err:
1792             BitString().decode(
1793                 tag_encode(3, form=TagFormConstructed) + LENINDEF + chunks * bs,
1794                 offset=offset,
1795                 decode_path=decode_path,
1796                 ctx={"bered": True},
1797             )
1798         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
1799         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
1800
1801     @given(
1802         integers(min_value=0),
1803         decode_path_strat,
1804         integers(min_value=1, max_value=3),
1805     )
1806     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
1807         bs = BitString(b"data").encode()
1808         bs_longer = BitString(b"data-longer").encode()
1809         with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
1810             BitString().decode(
1811                 (
1812                     tag_encode(3, form=TagFormConstructed) +
1813                     len_encode((chunks + 1) * len(bs)) +
1814                     chunks * bs +
1815                     bs_longer
1816                 ),
1817                 offset=offset,
1818                 decode_path=decode_path,
1819                 ctx={"bered": True},
1820             )
1821         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
1822         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
1823
1824     @given(
1825         integers(min_value=0),
1826         decode_path_strat,
1827     )
1828     def test_ber_indefinite_no_chunks(self, offset, decode_path):
1829         with assertRaisesRegex(self, DecodeError, "no chunks") as err:
1830             BitString().decode(
1831                 tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
1832                 offset=offset,
1833                 decode_path=decode_path,
1834                 ctx={"bered": True},
1835             )
1836         self.assertEqual(err.exception.decode_path, decode_path)
1837         self.assertEqual(err.exception.offset, offset)
1838
1839     @given(data_strategy())
1840     def test_ber_indefinite_not_multiple(self, d):
1841         bs_short = BitString("'A'H").encode()
1842         bs_full = BitString("'AA'H").encode()
1843         chunks = [bs_full for _ in range(d.draw(integers(min_value=0, max_value=3)))]
1844         chunks.append(bs_short)
1845         d.draw(permutations(chunks))
1846         chunks.append(bs_short)
1847         offset = d.draw(integers(min_value=0))
1848         decode_path = d.draw(decode_path_strat)
1849         with assertRaisesRegex(self, DecodeError, "multiple of 8 bits") as err:
1850             BitString().decode(
1851                 (
1852                     tag_encode(3, form=TagFormConstructed) +
1853                     LENINDEF +
1854                     b"".join(chunks) +
1855                     EOC
1856                 ),
1857                 offset=offset,
1858                 decode_path=decode_path,
1859                 ctx={"bered": True},
1860             )
1861         self.assertEqual(
1862             err.exception.decode_path,
1863             decode_path + (str(chunks.index(bs_short)),),
1864         )
1865         self.assertEqual(
1866             err.exception.offset,
1867             offset + 1 + 1 + chunks.index(bs_short) * len(bs_full),
1868         )
1869
1870     def test_x690_vector(self):
1871         vector = BitString("'0A3B5F291CD'H")
1872         obj, tail = BitString().decode(hexdec("0307040A3B5F291CD0"))
1873         self.assertSequenceEqual(tail, b"")
1874         self.assertEqual(obj, vector)
1875         obj, tail = BitString().decode(
1876             hexdec("23800303000A3B0305045F291CD00000"),
1877             ctx={"bered": True},
1878         )
1879         self.assertSequenceEqual(tail, b"")
1880         self.assertEqual(obj, vector)
1881         self.assertTrue(obj.ber_encoded)
1882         self.assertTrue(obj.lenindef)
1883         self.assertTrue(obj.bered)
1884         obj = copy(obj)
1885         self.assertTrue(obj.ber_encoded)
1886         self.assertTrue(obj.lenindef)
1887         self.assertTrue(obj.bered)
1888
1889     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1890     @given(integers(min_value=1000, max_value=3000))
1891     def test_cer(self, data_len):
1892         data = urandom(data_len)
1893         encoded = encode_cer(BitString(data))
1894         ctx = {"bered": True}
1895         self.assertSequenceEqual(bytes(BitString().decod(encoded, ctx=ctx)), data)
1896         evgens = list(BitString().decode_evgen(encoded, ctx=ctx))
1897         evgens_expected = data_len // 999
1898         if evgens_expected * 999 != data_len:
1899             evgens_expected += 1
1900         evgens_expected += 1
1901         self.assertEqual(len(evgens), evgens_expected)
1902         for (_, obj, _) in evgens[:-2]:
1903             self.assertEqual(obj.vlen, 1000)
1904         _, obj, _ = evgens[-2]
1905         self.assertEqual(obj.vlen, 1 + data_len - len(evgens[:-2]) * 999)
1906
1907
1908 @composite
1909 def octet_string_values_strategy(draw, do_expl=False):
1910     bound_min, bound_max = sorted(draw(sets(
1911         integers(min_value=0, max_value=1 << 7),
1912         min_size=2,
1913         max_size=2,
1914     )))
1915     value = draw(one_of(
1916         none(),
1917         binary(min_size=bound_min, max_size=bound_max),
1918     ))
1919     default = draw(one_of(
1920         none(),
1921         binary(min_size=bound_min, max_size=bound_max),
1922     ))
1923     bounds = None
1924     if draw(booleans()):
1925         bounds = (bound_min, bound_max)
1926     impl = None
1927     expl = None
1928     if do_expl:
1929         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1930     else:
1931         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1932     optional = draw(one_of(none(), booleans()))
1933     _decoded = (
1934         draw(integers(min_value=0)),
1935         draw(integers(min_value=0)),
1936         draw(integers(min_value=0)),
1937     )
1938     return (value, bounds, impl, expl, default, optional, _decoded)
1939
1940
1941 class OctetStringInherited(OctetString):
1942     pass
1943
1944
1945 class TestOctetString(CommonMixin, TestCase):
1946     base_klass = OctetString
1947
1948     def test_invalid_value_type(self):
1949         with self.assertRaises(InvalidValueType) as err:
1950             OctetString(text_type(123))
1951         repr(err.exception)
1952
1953     @given(booleans())
1954     def test_optional(self, optional):
1955         obj = OctetString(default=OctetString(b""), optional=optional)
1956         self.assertTrue(obj.optional)
1957
1958     @given(binary())
1959     def test_ready(self, value):
1960         obj = OctetString()
1961         self.assertFalse(obj.ready)
1962         repr(obj)
1963         list(obj.pps())
1964         pprint(obj, big_blobs=True, with_decode_path=True)
1965         with self.assertRaises(ObjNotReady) as err:
1966             obj.encode()
1967         repr(err.exception)
1968         obj = OctetString(value)
1969         self.assertTrue(obj.ready)
1970         repr(obj)
1971         list(obj.pps())
1972         pprint(obj, big_blobs=True, with_decode_path=True)
1973
1974     @given(binary(), binary(), binary(min_size=1), binary(min_size=1))
1975     def test_comparison(self, value1, value2, tag1, tag2):
1976         for klass in (OctetString, OctetStringInherited):
1977             obj1 = klass(value1)
1978             obj2 = klass(value2)
1979             self.assertEqual(obj1 == obj2, value1 == value2)
1980             self.assertEqual(obj1 != obj2, value1 != value2)
1981             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
1982             obj1 = klass(value1, impl=tag1)
1983             obj2 = klass(value1, impl=tag2)
1984             self.assertEqual(obj1 == obj2, tag1 == tag2)
1985             self.assertEqual(obj1 != obj2, tag1 != tag2)
1986
1987     @given(lists(binary()))
1988     def test_sorted_works(self, values):
1989         self.assertSequenceEqual(
1990             [bytes(v) for v in sorted(OctetString(v) for v in values)],
1991             sorted(values),
1992         )
1993
1994     @given(data_strategy())
1995     def test_bounds_satisfied(self, d):
1996         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
1997         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1998         value = d.draw(binary(min_size=bound_min, max_size=bound_max))
1999         OctetString(value=value, bounds=(bound_min, bound_max))
2000
2001     @given(data_strategy())
2002     def test_bounds_unsatisfied(self, d):
2003         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
2004         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
2005         value = d.draw(binary(max_size=bound_min - 1))
2006         with self.assertRaises(BoundsError) as err:
2007             OctetString(value=value, bounds=(bound_min, bound_max))
2008         repr(err.exception)
2009         with assertRaisesRegex(self, DecodeError, "bounds") as err:
2010             OctetString(bounds=(bound_min, bound_max)).decode(
2011                 OctetString(value).encode()
2012             )
2013         repr(err.exception)
2014         value = d.draw(binary(min_size=bound_max + 1))
2015         with self.assertRaises(BoundsError) as err:
2016             OctetString(value=value, bounds=(bound_min, bound_max))
2017         repr(err.exception)
2018         with assertRaisesRegex(self, DecodeError, "bounds") as err:
2019             OctetString(bounds=(bound_min, bound_max)).decode(
2020                 OctetString(value).encode()
2021             )
2022         repr(err.exception)
2023
2024     @given(data_strategy())
2025     def test_call(self, d):
2026         for klass in (OctetString, OctetStringInherited):
2027             (
2028                 value_initial,
2029                 bounds_initial,
2030                 impl_initial,
2031                 expl_initial,
2032                 default_initial,
2033                 optional_initial,
2034                 _decoded_initial,
2035             ) = d.draw(octet_string_values_strategy())
2036             obj_initial = klass(
2037                 value_initial,
2038                 bounds_initial,
2039                 impl_initial,
2040                 expl_initial,
2041                 default_initial,
2042                 optional_initial or False,
2043                 _decoded_initial,
2044             )
2045             (
2046                 value,
2047                 bounds,
2048                 impl,
2049                 expl,
2050                 default,
2051                 optional,
2052                 _decoded,
2053             ) = d.draw(octet_string_values_strategy(do_expl=impl_initial is None))
2054             if (default is None) and (obj_initial.default is not None):
2055                 bounds = None
2056             if (
2057                     (bounds is None) and
2058                     (value is not None) and
2059                     (bounds_initial is not None) and
2060                     not (bounds_initial[0] <= len(value) <= bounds_initial[1])
2061             ):
2062                 value = None
2063             if (
2064                     (bounds is None) and
2065                     (default is not None) and
2066                     (bounds_initial is not None) and
2067                     not (bounds_initial[0] <= len(default) <= bounds_initial[1])
2068             ):
2069                 default = None
2070             obj = obj_initial(value, bounds, impl, expl, default, optional)
2071             if obj.ready:
2072                 value_expected = default if value is None else value
2073                 value_expected = (
2074                     default_initial if value_expected is None
2075                     else value_expected
2076                 )
2077                 self.assertEqual(obj, value_expected)
2078             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2079             self.assertEqual(obj.expl_tag, expl or expl_initial)
2080             self.assertEqual(
2081                 obj.default,
2082                 default_initial if default is None else default,
2083             )
2084             if obj.default is None:
2085                 optional = optional_initial if optional is None else optional
2086                 optional = False if optional is None else optional
2087             else:
2088                 optional = True
2089             self.assertEqual(obj.optional, optional)
2090             self.assertEqual(
2091                 (obj._bound_min, obj._bound_max),
2092                 bounds or bounds_initial or (0, float("+inf")),
2093             )
2094
2095     @given(octet_string_values_strategy())
2096     def test_copy(self, values):
2097         for klass in (OctetString, OctetStringInherited):
2098             obj = klass(*values)
2099             for copy_func in copy_funcs:
2100                 obj_copied = copy_func(obj)
2101                 self.assert_copied_basic_fields(obj, obj_copied)
2102                 self.assertEqual(obj._bound_min, obj_copied._bound_min)
2103                 self.assertEqual(obj._bound_max, obj_copied._bound_max)
2104                 self.assertEqual(obj._value, obj_copied._value)
2105
2106     @given(
2107         binary(),
2108         integers(min_value=1).map(tag_encode),
2109     )
2110     def test_stripped(self, value, tag_impl):
2111         obj = OctetString(value, impl=tag_impl)
2112         with self.assertRaises(NotEnoughData):
2113             obj.decode(obj.encode()[:-1])
2114
2115     @given(
2116         binary(),
2117         integers(min_value=1).map(tag_ctxc),
2118     )
2119     def test_stripped_expl(self, value, tag_expl):
2120         obj = OctetString(value, expl=tag_expl)
2121         with self.assertRaises(NotEnoughData):
2122             obj.decode(obj.encode()[:-1])
2123
2124     @given(
2125         integers(min_value=31),
2126         integers(min_value=0),
2127         decode_path_strat,
2128     )
2129     def test_bad_tag(self, tag, offset, decode_path):
2130         with self.assertRaises(DecodeError) as err:
2131             OctetString().decode(
2132                 tag_encode(tag)[:-1],
2133                 offset=offset,
2134                 decode_path=decode_path,
2135             )
2136         repr(err.exception)
2137         self.assertEqual(err.exception.offset, offset)
2138         self.assertEqual(err.exception.decode_path, decode_path)
2139
2140     @given(
2141         integers(min_value=128),
2142         integers(min_value=0),
2143         decode_path_strat,
2144     )
2145     def test_bad_len(self, l, offset, decode_path):
2146         with self.assertRaises(DecodeError) as err:
2147             OctetString().decode(
2148                 OctetString.tag_default + len_encode(l)[:-1],
2149                 offset=offset,
2150                 decode_path=decode_path,
2151             )
2152         repr(err.exception)
2153         self.assertEqual(err.exception.offset, offset)
2154         self.assertEqual(err.exception.decode_path, decode_path)
2155
2156     @given(
2157         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
2158         integers(min_value=0),
2159         decode_path_strat,
2160     )
2161     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
2162         value, bound_min = list(sorted(ints))
2163
2164         class String(OctetString):
2165             bounds = (bound_min, bound_min)
2166         with self.assertRaises(DecodeError) as err:
2167             String().decode(
2168                 OctetString(b"\x00" * value).encode(),
2169                 offset=offset,
2170                 decode_path=decode_path,
2171             )
2172         repr(err.exception)
2173         self.assertEqual(err.exception.offset, offset)
2174         self.assertEqual(err.exception.decode_path, decode_path)
2175
2176     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2177     @given(
2178         octet_string_values_strategy(),
2179         binary(),
2180         integers(min_value=1).map(tag_ctxc),
2181         integers(min_value=0),
2182         binary(max_size=5),
2183         decode_path_strat,
2184     )
2185     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
2186         for klass in (OctetString, OctetStringInherited):
2187             _, _, _, _, default, optional, _decoded = values
2188             obj = klass(
2189                 value=value,
2190                 default=default,
2191                 optional=optional,
2192                 _decoded=_decoded,
2193             )
2194             repr(obj)
2195             list(obj.pps())
2196             pprint(obj, big_blobs=True, with_decode_path=True)
2197             self.assertFalse(obj.expled)
2198             obj_encoded = obj.encode()
2199             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
2200             obj_expled = obj(value, expl=tag_expl)
2201             self.assertTrue(obj_expled.expled)
2202             repr(obj_expled)
2203             list(obj_expled.pps())
2204             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2205             obj_expled_encoded = obj_expled.encode()
2206             obj_expled_cer = encode_cer(obj_expled)
2207             self.assertNotEqual(obj_expled_cer, obj_encoded)
2208             self.assertSequenceEqual(
2209                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
2210                 obj_expled_encoded,
2211             )
2212             ctx_copied = deepcopy(ctx_dummy)
2213             obj_decoded, tail = obj_expled.decode(
2214                 obj_expled_encoded + tail_junk,
2215                 offset=offset,
2216                 ctx=ctx_copied,
2217             )
2218             self.assertDictEqual(ctx_copied, ctx_dummy)
2219             repr(obj_decoded)
2220             list(obj_decoded.pps())
2221             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2222             self.assertEqual(tail, tail_junk)
2223             self.assertEqual(obj_decoded, obj_expled)
2224             self.assertNotEqual(obj_decoded, obj)
2225             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
2226             self.assertEqual(bytes(obj_decoded), bytes(obj))
2227             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2228             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2229             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2230             self.assertEqual(
2231                 obj_decoded.expl_llen,
2232                 len(len_encode(len(obj_encoded))),
2233             )
2234             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2235             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2236             self.assertEqual(
2237                 obj_decoded.offset,
2238                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2239             )
2240             self.assertEqual(obj_decoded.expl_offset, offset)
2241             assert_exceeding_data(
2242                 self,
2243                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2244                 tail_junk,
2245             )
2246
2247             evgens = list(obj_expled.decode_evgen(
2248                 obj_expled_encoded + tail_junk,
2249                 offset=offset,
2250                 decode_path=decode_path,
2251                 ctx=ctx_copied,
2252             ))
2253             self.assertEqual(len(evgens), 1)
2254             _decode_path, obj, tail = evgens[0]
2255             self.assertSequenceEqual(tail, tail_junk)
2256             self.assertEqual(_decode_path, decode_path)
2257             self.assertEqual(obj.expl_offset, offset)
2258             repr(obj)
2259             list(obj.pps())
2260
2261     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2262     @given(
2263         integers(min_value=1, max_value=30),
2264         lists(
2265             one_of(
2266                 binary(min_size=1, max_size=5),
2267                 lists(
2268                     binary(min_size=1, max_size=5),
2269                     min_size=1,
2270                     max_size=3,
2271                 ),
2272             ),
2273             min_size=1,
2274             max_size=3,
2275         ),
2276         binary(),
2277         decode_path_strat,
2278     )
2279     def test_constructed(self, impl, chunk_inputs, junk, decode_path):
2280         def chunk_constructed(contents):
2281             return (
2282                 tag_encode(form=TagFormConstructed, num=4) +
2283                 LENINDEF +
2284                 b"".join(OctetString(content).encode() for content in contents) +
2285                 EOC
2286             )
2287         chunks = []
2288         chunks_len_expected = []
2289         payload_expected = b""
2290         for chunk_input in chunk_inputs:
2291             if isinstance(chunk_input, binary_type):
2292                 chunks.append(OctetString(chunk_input).encode())
2293                 payload_expected += chunk_input
2294                 chunks_len_expected.append(len(chunk_input))
2295             else:
2296                 chunks.append(chunk_constructed(chunk_input))
2297                 payload = b"".join(chunk_input)
2298                 payload_expected += payload
2299                 for c in chunk_input:
2300                     chunks_len_expected.append(len(c))
2301                 chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
2302         encoded_indefinite = (
2303             tag_encode(form=TagFormConstructed, num=impl) +
2304             LENINDEF +
2305             b"".join(chunks) +
2306             EOC
2307         )
2308         encoded_definite = (
2309             tag_encode(form=TagFormConstructed, num=impl) +
2310             len_encode(len(b"".join(chunks))) +
2311             b"".join(chunks)
2312         )
2313         with assertRaisesRegex(self, DecodeError, "unallowed BER"):
2314             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
2315         for lenindef_expected, encoded in (
2316                 (True, encoded_indefinite),
2317                 (False, encoded_definite),
2318         ):
2319             obj, tail = OctetString(impl=tag_encode(impl)).decode(
2320                 encoded + junk,
2321                 ctx={"bered": True},
2322             )
2323             self.assertSequenceEqual(tail, junk)
2324             self.assertSequenceEqual(bytes(obj), payload_expected)
2325             self.assertTrue(obj.ber_encoded)
2326             self.assertEqual(obj.lenindef, lenindef_expected)
2327             self.assertTrue(obj.bered)
2328             obj = copy(obj)
2329             self.assertTrue(obj.ber_encoded)
2330             self.assertEqual(obj.lenindef, lenindef_expected)
2331             self.assertTrue(obj.bered)
2332             self.assertEqual(len(encoded), obj.tlvlen)
2333             repr(obj)
2334             list(obj.pps())
2335             pprint(obj, big_blobs=True, with_decode_path=True)
2336
2337             evgens = list(OctetString(impl=tag_encode(impl)).decode_evgen(
2338                 encoded,
2339                 decode_path=decode_path,
2340                 ctx={"bered": True},
2341             ))
2342             self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
2343             for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
2344                 self.assertGreater(len(dp), len(decode_path))
2345                 self.assertEqual(obj.vlen, chunk_len_expected)
2346
2347     @given(
2348         integers(min_value=0),
2349         decode_path_strat,
2350     )
2351     def test_ber_definite_too_short(self, offset, decode_path):
2352         with assertRaisesRegex(self, DecodeError, "longer than data") as err:
2353             OctetString().decode(
2354                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
2355                 offset=offset,
2356                 decode_path=decode_path,
2357                 ctx={"bered": True},
2358             )
2359         self.assertEqual(err.exception.decode_path, decode_path)
2360         self.assertEqual(err.exception.offset, offset)
2361
2362     @given(
2363         integers(min_value=0),
2364         decode_path_strat,
2365         integers(min_value=1, max_value=3),
2366     )
2367     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
2368         bs = OctetString(b"data").encode()
2369         with self.assertRaises(NotEnoughData) as err:
2370             OctetString().decode(
2371                 tag_encode(4, form=TagFormConstructed) + LENINDEF + chunks * bs,
2372                 offset=offset,
2373                 decode_path=decode_path,
2374                 ctx={"bered": True},
2375             )
2376         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2377         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2378
2379     @given(
2380         integers(min_value=0),
2381         decode_path_strat,
2382         integers(min_value=1, max_value=3),
2383     )
2384     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
2385         bs = OctetString(b"data").encode()
2386         bs_longer = OctetString(b"data-longer").encode()
2387         with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
2388             OctetString().decode(
2389                 (
2390                     tag_encode(4, form=TagFormConstructed) +
2391                     len_encode((chunks + 1) * len(bs)) +
2392                     chunks * bs +
2393                     bs_longer
2394                 ),
2395                 offset=offset,
2396                 decode_path=decode_path,
2397                 ctx={"bered": True},
2398             )
2399         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2400         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2401
2402     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2403     @given(integers(min_value=1001, max_value=3000))
2404     def test_cer(self, data_len):
2405         data = urandom(data_len)
2406         encoded = encode_cer(OctetString(data))
2407         ctx = {"bered": True}
2408         self.assertSequenceEqual(bytes(OctetString().decod(encoded, ctx=ctx)), data)
2409         evgens = list(OctetString().decode_evgen(encoded, ctx=ctx))
2410         evgens_expected = data_len // 1000
2411         if evgens_expected * 1000 != data_len:
2412             evgens_expected += 1
2413         evgens_expected += 1
2414         self.assertEqual(len(evgens), evgens_expected)
2415         for (_, obj, _) in evgens[:-2]:
2416             self.assertEqual(obj.vlen, 1000)
2417         _, obj, _ = evgens[-2]
2418         self.assertEqual(obj.vlen, data_len - len(evgens[:-2]) * 1000)
2419
2420
2421 @composite
2422 def null_values_strategy(draw, do_expl=False):
2423     impl = None
2424     expl = None
2425     if do_expl:
2426         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2427     else:
2428         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2429     optional = draw(one_of(none(), booleans()))
2430     _decoded = (
2431         draw(integers(min_value=0)),
2432         draw(integers(min_value=0)),
2433         draw(integers(min_value=0)),
2434     )
2435     return (impl, expl, optional, _decoded)
2436
2437
2438 class NullInherited(Null):
2439     pass
2440
2441
2442 class TestNull(CommonMixin, TestCase):
2443     base_klass = Null
2444
2445     def test_ready(self):
2446         obj = Null()
2447         self.assertTrue(obj.ready)
2448         repr(obj)
2449         list(obj.pps())
2450         pprint(obj, big_blobs=True, with_decode_path=True)
2451
2452     @given(binary(min_size=1), binary(min_size=1))
2453     def test_comparison(self, tag1, tag2):
2454         for klass in (Null, NullInherited):
2455             obj1 = klass(impl=tag1)
2456             obj2 = klass(impl=tag2)
2457             self.assertEqual(obj1 == obj2, tag1 == tag2)
2458             self.assertEqual(obj1 != obj2, tag1 != tag2)
2459             self.assertNotEqual(obj1, tag2)
2460
2461     @given(data_strategy())
2462     def test_call(self, d):
2463         for klass in (Null, NullInherited):
2464             (
2465                 impl_initial,
2466                 expl_initial,
2467                 optional_initial,
2468                 _decoded_initial,
2469             ) = d.draw(null_values_strategy())
2470             obj_initial = klass(
2471                 impl=impl_initial,
2472                 expl=expl_initial,
2473                 optional=optional_initial or False,
2474                 _decoded=_decoded_initial,
2475             )
2476             (
2477                 impl,
2478                 expl,
2479                 optional,
2480                 _decoded,
2481             ) = d.draw(null_values_strategy(do_expl=impl_initial is None))
2482             obj = obj_initial(impl=impl, expl=expl, optional=optional)
2483             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2484             self.assertEqual(obj.expl_tag, expl or expl_initial)
2485             optional = optional_initial if optional is None else optional
2486             optional = False if optional is None else optional
2487             self.assertEqual(obj.optional, optional)
2488
2489     @given(null_values_strategy())
2490     def test_copy(self, values):
2491         for klass in (Null, NullInherited):
2492             impl, expl, optional, _decoded = values
2493             obj = klass(
2494                 impl=impl,
2495                 expl=expl,
2496                 optional=optional or False,
2497                 _decoded=_decoded,
2498             )
2499             for copy_func in copy_funcs:
2500                 obj_copied = copy_func(obj)
2501                 self.assert_copied_basic_fields(obj, obj_copied)
2502
2503     @given(integers(min_value=1).map(tag_encode))
2504     def test_stripped(self, tag_impl):
2505         obj = Null(impl=tag_impl)
2506         with self.assertRaises(NotEnoughData):
2507             obj.decode(obj.encode()[:-1])
2508
2509     @given(integers(min_value=1).map(tag_ctxc))
2510     def test_stripped_expl(self, tag_expl):
2511         obj = Null(expl=tag_expl)
2512         with self.assertRaises(NotEnoughData):
2513             obj.decode(obj.encode()[:-1])
2514
2515     @given(
2516         integers(min_value=31),
2517         integers(min_value=0),
2518         decode_path_strat,
2519     )
2520     def test_bad_tag(self, tag, offset, decode_path):
2521         with self.assertRaises(DecodeError) as err:
2522             Null().decode(
2523                 tag_encode(tag)[:-1],
2524                 offset=offset,
2525                 decode_path=decode_path,
2526             )
2527         repr(err.exception)
2528         self.assertEqual(err.exception.offset, offset)
2529         self.assertEqual(err.exception.decode_path, decode_path)
2530
2531     @given(
2532         integers(min_value=128),
2533         integers(min_value=0),
2534         decode_path_strat,
2535     )
2536     def test_bad_len(self, l, offset, decode_path):
2537         with self.assertRaises(DecodeError) as err:
2538             Null().decode(
2539                 Null.tag_default + len_encode(l)[:-1],
2540                 offset=offset,
2541                 decode_path=decode_path,
2542             )
2543         repr(err.exception)
2544         self.assertEqual(err.exception.offset, offset)
2545         self.assertEqual(err.exception.decode_path, decode_path)
2546
2547     @given(binary(min_size=1))
2548     def test_tag_mismatch(self, impl):
2549         assume(impl != Null.tag_default)
2550         with self.assertRaises(TagMismatch):
2551             Null(impl=impl).decode(Null().encode())
2552
2553     @given(
2554         null_values_strategy(),
2555         integers(min_value=1).map(tag_ctxc),
2556         integers(min_value=0),
2557         binary(max_size=5),
2558         decode_path_strat,
2559     )
2560     def test_symmetric(self, values, tag_expl, offset, tail_junk, decode_path):
2561         for klass in (Null, NullInherited):
2562             _, _, optional, _decoded = values
2563             obj = klass(optional=optional, _decoded=_decoded)
2564             repr(obj)
2565             list(obj.pps())
2566             pprint(obj, big_blobs=True, with_decode_path=True)
2567             self.assertFalse(obj.expled)
2568             obj_encoded = obj.encode()
2569             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
2570             obj_expled = obj(expl=tag_expl)
2571             self.assertTrue(obj_expled.expled)
2572             repr(obj_expled)
2573             list(obj_expled.pps())
2574             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2575             obj_expled_encoded = obj_expled.encode()
2576             obj_expled_cer = encode_cer(obj_expled)
2577             self.assertNotEqual(obj_expled_cer, obj_encoded)
2578             self.assertSequenceEqual(
2579                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
2580                 obj_expled_encoded,
2581             )
2582             ctx_copied = deepcopy(ctx_dummy)
2583             obj_decoded, tail = obj_expled.decode(
2584                 obj_expled_encoded + tail_junk,
2585                 offset=offset,
2586                 ctx=ctx_copied,
2587             )
2588             self.assertDictEqual(ctx_copied, ctx_dummy)
2589             repr(obj_decoded)
2590             list(obj_decoded.pps())
2591             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2592             self.assertEqual(tail, tail_junk)
2593             self.assertEqual(obj_decoded, obj_expled)
2594             self.assertNotEqual(obj_decoded, obj)
2595             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2596             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2597             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2598             self.assertEqual(
2599                 obj_decoded.expl_llen,
2600                 len(len_encode(len(obj_encoded))),
2601             )
2602             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2603             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2604             self.assertEqual(
2605                 obj_decoded.offset,
2606                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2607             )
2608             self.assertEqual(obj_decoded.expl_offset, offset)
2609             assert_exceeding_data(
2610                 self,
2611                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2612                 tail_junk,
2613             )
2614
2615             evgens = list(obj_expled.decode_evgen(
2616                 obj_expled_encoded + tail_junk,
2617                 offset=offset,
2618                 decode_path=decode_path,
2619                 ctx=ctx_copied,
2620             ))
2621             self.assertEqual(len(evgens), 1)
2622             _decode_path, obj, tail = evgens[0]
2623             self.assertSequenceEqual(tail, tail_junk)
2624             self.assertEqual(_decode_path, decode_path)
2625             self.assertEqual(obj, obj_decoded)
2626             self.assertEqual(obj.expl_offset, offset)
2627             repr(obj)
2628             list(obj.pps())
2629
2630
2631     @given(integers(min_value=1))
2632     def test_invalid_len(self, l):
2633         with self.assertRaises(InvalidLength):
2634             Null().decode(b"".join((
2635                 Null.tag_default,
2636                 len_encode(l),
2637             )))
2638
2639
2640 @composite
2641 def oid_strategy(draw):
2642     first_arc = draw(integers(min_value=0, max_value=2))
2643     second_arc = 0
2644     if first_arc in (0, 1):
2645         second_arc = draw(integers(min_value=0, max_value=39))
2646     else:
2647         second_arc = draw(integers(min_value=0))
2648     other_arcs = draw(lists(integers(min_value=0)))
2649     return tuple([first_arc, second_arc] + other_arcs)
2650
2651
2652 @composite
2653 def oid_values_strategy(draw, do_expl=False):
2654     value = draw(one_of(none(), oid_strategy()))
2655     impl = None
2656     expl = None
2657     if do_expl:
2658         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2659     else:
2660         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2661     default = draw(one_of(none(), oid_strategy()))
2662     optional = draw(one_of(none(), booleans()))
2663     _decoded = (
2664         draw(integers(min_value=0)),
2665         draw(integers(min_value=0)),
2666         draw(integers(min_value=0)),
2667     )
2668     return (value, impl, expl, default, optional, _decoded)
2669
2670
2671 class ObjectIdentifierInherited(ObjectIdentifier):
2672     pass
2673
2674
2675 class TestObjectIdentifier(CommonMixin, TestCase):
2676     base_klass = ObjectIdentifier
2677
2678     def test_invalid_value_type(self):
2679         with self.assertRaises(InvalidValueType) as err:
2680             ObjectIdentifier(123)
2681         repr(err.exception)
2682
2683     @given(booleans())
2684     def test_optional(self, optional):
2685         obj = ObjectIdentifier(default=ObjectIdentifier("1.2.3"), optional=optional)
2686         self.assertTrue(obj.optional)
2687
2688     @given(oid_strategy())
2689     def test_ready(self, value):
2690         obj = ObjectIdentifier()
2691         self.assertFalse(obj.ready)
2692         repr(obj)
2693         list(obj.pps())
2694         pprint(obj, big_blobs=True, with_decode_path=True)
2695         with self.assertRaises(ObjNotReady) as err:
2696             obj.encode()
2697         repr(err.exception)
2698         obj = ObjectIdentifier(value)
2699         self.assertTrue(obj.ready)
2700         self.assertFalse(obj.ber_encoded)
2701         repr(obj)
2702         list(obj.pps())
2703         pprint(obj, big_blobs=True, with_decode_path=True)
2704         hash(obj)
2705
2706     @given(oid_strategy(), oid_strategy(), binary(min_size=1), binary(min_size=1))
2707     def test_comparison(self, value1, value2, tag1, tag2):
2708         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2709             obj1 = klass(value1)
2710             obj2 = klass(value2)
2711             self.assertEqual(obj1 == obj2, value1 == value2)
2712             self.assertEqual(obj1 != obj2, value1 != value2)
2713             self.assertEqual(obj1 == tuple(obj2), value1 == value2)
2714             self.assertEqual(str(obj1) == str(obj2), value1 == value2)
2715             obj1 = klass(value1, impl=tag1)
2716             obj2 = klass(value1, impl=tag2)
2717             self.assertEqual(obj1 == obj2, tag1 == tag2)
2718             self.assertEqual(obj1 != obj2, tag1 != tag2)
2719
2720     @given(lists(oid_strategy()))
2721     def test_sorted_works(self, values):
2722         self.assertSequenceEqual(
2723             [tuple(v) for v in sorted(ObjectIdentifier(v) for v in values)],
2724             sorted(values),
2725         )
2726
2727     @given(data_strategy())
2728     def test_call(self, d):
2729         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2730             (
2731                 value_initial,
2732                 impl_initial,
2733                 expl_initial,
2734                 default_initial,
2735                 optional_initial,
2736                 _decoded_initial,
2737             ) = d.draw(oid_values_strategy())
2738             obj_initial = klass(
2739                 value=value_initial,
2740                 impl=impl_initial,
2741                 expl=expl_initial,
2742                 default=default_initial,
2743                 optional=optional_initial or False,
2744                 _decoded=_decoded_initial,
2745             )
2746             (
2747                 value,
2748                 impl,
2749                 expl,
2750                 default,
2751                 optional,
2752                 _decoded,
2753             ) = d.draw(oid_values_strategy(do_expl=impl_initial is None))
2754             obj = obj_initial(
2755                 value=value,
2756                 impl=impl,
2757                 expl=expl,
2758                 default=default,
2759                 optional=optional,
2760             )
2761             if obj.ready:
2762                 value_expected = default if value is None else value
2763                 value_expected = (
2764                     default_initial if value_expected is None
2765                     else value_expected
2766                 )
2767                 self.assertEqual(obj, value_expected)
2768             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2769             self.assertEqual(obj.expl_tag, expl or expl_initial)
2770             self.assertEqual(
2771                 obj.default,
2772                 default_initial if default is None else default,
2773             )
2774             if obj.default is None:
2775                 optional = optional_initial if optional is None else optional
2776                 optional = False if optional is None else optional
2777             else:
2778                 optional = True
2779             self.assertEqual(obj.optional, optional)
2780
2781     @given(oid_values_strategy())
2782     def test_copy(self, values):
2783         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2784             (
2785                 value,
2786                 impl,
2787                 expl,
2788                 default,
2789                 optional,
2790                 _decoded,
2791             ) = values
2792             obj = klass(
2793                 value=value,
2794                 impl=impl,
2795                 expl=expl,
2796                 default=default,
2797                 optional=optional,
2798                 _decoded=_decoded,
2799             )
2800             for copy_func in copy_funcs:
2801                 obj_copied = copy_func(obj)
2802                 self.assert_copied_basic_fields(obj, obj_copied)
2803                 self.assertEqual(obj._value, obj_copied._value)
2804
2805     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2806     @given(
2807         oid_strategy(),
2808         integers(min_value=1).map(tag_encode),
2809     )
2810     def test_stripped(self, value, tag_impl):
2811         obj = ObjectIdentifier(value, impl=tag_impl)
2812         with self.assertRaises(NotEnoughData):
2813             obj.decode(obj.encode()[:-1])
2814
2815     @given(
2816         oid_strategy(),
2817         integers(min_value=1).map(tag_ctxc),
2818     )
2819     def test_stripped_expl(self, value, tag_expl):
2820         obj = ObjectIdentifier(value, expl=tag_expl)
2821         with self.assertRaises(NotEnoughData):
2822             obj.decode(obj.encode()[:-1])
2823
2824     @given(
2825         integers(min_value=31),
2826         integers(min_value=0),
2827         decode_path_strat,
2828     )
2829     def test_bad_tag(self, tag, offset, decode_path):
2830         with self.assertRaises(DecodeError) as err:
2831             ObjectIdentifier().decode(
2832                 tag_encode(tag)[:-1],
2833                 offset=offset,
2834                 decode_path=decode_path,
2835             )
2836         repr(err.exception)
2837         self.assertEqual(err.exception.offset, offset)
2838         self.assertEqual(err.exception.decode_path, decode_path)
2839
2840     @given(
2841         integers(min_value=128),
2842         integers(min_value=0),
2843         decode_path_strat,
2844     )
2845     def test_bad_len(self, l, offset, decode_path):
2846         with self.assertRaises(DecodeError) as err:
2847             ObjectIdentifier().decode(
2848                 ObjectIdentifier.tag_default + len_encode(l)[:-1],
2849                 offset=offset,
2850                 decode_path=decode_path,
2851             )
2852         repr(err.exception)
2853         self.assertEqual(err.exception.offset, offset)
2854         self.assertEqual(err.exception.decode_path, decode_path)
2855
2856     def test_zero_oid(self):
2857         with self.assertRaises(NotEnoughData):
2858             ObjectIdentifier().decode(
2859                 b"".join((ObjectIdentifier.tag_default, len_encode(0)))
2860             )
2861
2862     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2863     @given(oid_strategy())
2864     def test_unfinished_oid(self, value):
2865         assume(list(value)[-1] > 255)
2866         obj_encoded = ObjectIdentifier(value).encode()
2867         obj, _ = ObjectIdentifier().decode(obj_encoded)
2868         data = obj_encoded[obj.tlen + obj.llen:-1]
2869         data = b"".join((
2870             ObjectIdentifier.tag_default,
2871             len_encode(len(data)),
2872             data,
2873         ))
2874         with assertRaisesRegex(self, DecodeError, "unfinished OID"):
2875             obj.decode(data)
2876
2877     @given(integers(min_value=0))
2878     def test_invalid_short(self, value):
2879         with self.assertRaises(InvalidOID):
2880             ObjectIdentifier((value,))
2881         with self.assertRaises(InvalidOID):
2882             ObjectIdentifier("%d" % value)
2883
2884     @given(integers(min_value=3), integers(min_value=0))
2885     def test_invalid_first_arc(self, first_arc, second_arc):
2886         with self.assertRaises(InvalidOID):
2887             ObjectIdentifier((first_arc, second_arc))
2888         with self.assertRaises(InvalidOID):
2889             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2890
2891     @given(integers(min_value=0, max_value=1), integers(min_value=40))
2892     def test_invalid_second_arc(self, first_arc, second_arc):
2893         with self.assertRaises(InvalidOID):
2894             ObjectIdentifier((first_arc, second_arc))
2895         with self.assertRaises(InvalidOID):
2896             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2897
2898     @given(text(alphabet=ascii_letters + ".", min_size=1))
2899     def test_junk(self, oid):
2900         with self.assertRaises(InvalidOID):
2901             ObjectIdentifier(oid)
2902
2903     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2904     @given(oid_strategy())
2905     def test_validness(self, oid):
2906         obj = ObjectIdentifier(oid)
2907         self.assertEqual(obj, ObjectIdentifier(".".join(str(arc) for arc in oid)))
2908         str(obj)
2909         repr(obj)
2910         pprint(obj, big_blobs=True, with_decode_path=True)
2911
2912     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2913     @given(
2914         oid_values_strategy(),
2915         oid_strategy(),
2916         integers(min_value=1).map(tag_ctxc),
2917         integers(min_value=0),
2918         binary(max_size=5),
2919         decode_path_strat,
2920     )
2921     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
2922         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2923             _, _, _, default, optional, _decoded = values
2924             obj = klass(
2925                 value=value,
2926                 default=default,
2927                 optional=optional,
2928                 _decoded=_decoded,
2929             )
2930             repr(obj)
2931             list(obj.pps())
2932             pprint(obj, big_blobs=True, with_decode_path=True)
2933             self.assertFalse(obj.expled)
2934             obj_encoded = obj.encode()
2935             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
2936             obj_expled = obj(value, expl=tag_expl)
2937             self.assertTrue(obj_expled.expled)
2938             repr(obj_expled)
2939             list(obj_expled.pps())
2940             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2941             obj_expled_encoded = obj_expled.encode()
2942             obj_expled_cer = encode_cer(obj_expled)
2943             self.assertNotEqual(obj_expled_cer, obj_encoded)
2944             self.assertSequenceEqual(
2945                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
2946                 obj_expled_encoded,
2947             )
2948             ctx_copied = deepcopy(ctx_dummy)
2949             obj_decoded, tail = obj_expled.decode(
2950                 obj_expled_encoded + tail_junk,
2951                 offset=offset,
2952                 ctx=ctx_copied,
2953             )
2954             self.assertDictEqual(ctx_copied, ctx_dummy)
2955             repr(obj_decoded)
2956             list(obj_decoded.pps())
2957             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2958             self.assertEqual(tail, tail_junk)
2959             self.assertEqual(obj_decoded, obj_expled)
2960             self.assertNotEqual(obj_decoded, obj)
2961             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
2962             self.assertEqual(tuple(obj_decoded), tuple(obj))
2963             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2964             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2965             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2966             self.assertEqual(
2967                 obj_decoded.expl_llen,
2968                 len(len_encode(len(obj_encoded))),
2969             )
2970             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2971             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2972             self.assertEqual(
2973                 obj_decoded.offset,
2974                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2975             )
2976             self.assertEqual(obj_decoded.expl_offset, offset)
2977             assert_exceeding_data(
2978                 self,
2979                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2980                 tail_junk,
2981             )
2982
2983             evgens = list(obj_expled.decode_evgen(
2984                 obj_expled_encoded + tail_junk,
2985                 offset=offset,
2986                 decode_path=decode_path,
2987                 ctx=ctx_copied,
2988             ))
2989             self.assertEqual(len(evgens), 1)
2990             _decode_path, obj, tail = evgens[0]
2991             self.assertSequenceEqual(tail, tail_junk)
2992             self.assertEqual(_decode_path, decode_path)
2993             self.assertEqual(obj, obj_decoded)
2994             self.assertEqual(obj.expl_offset, offset)
2995             repr(obj)
2996             list(obj.pps())
2997
2998     @given(
2999         oid_strategy().map(ObjectIdentifier),
3000         oid_strategy().map(ObjectIdentifier),
3001     )
3002     def test_add(self, oid1, oid2):
3003         oid_expect = ObjectIdentifier(str(oid1) + "." + str(oid2))
3004         for oid_to_add in (oid2, tuple(oid2)):
3005             self.assertEqual(oid1 + oid_to_add, oid_expect)
3006         with self.assertRaises(InvalidValueType):
3007             oid1 + str(oid2)
3008
3009     def test_go_vectors_valid(self):
3010         for data, expect in (
3011                 (b"\x55", (2, 5)),
3012                 (b"\x55\x02", (2, 5, 2)),
3013                 (b"\x55\x02\xc0\x00", (2, 5, 2, 8192)),
3014                 (b"\x81\x34\x03", (2, 100, 3)),
3015         ):
3016             self.assertEqual(
3017                 ObjectIdentifier().decode(b"".join((
3018                     ObjectIdentifier.tag_default,
3019                     len_encode(len(data)),
3020                     data,
3021                 )))[0],
3022                 expect,
3023             )
3024
3025     def test_go_vectors_invalid(self):
3026         data = b"\x55\x02\xc0\x80\x80\x80\x80"
3027         with self.assertRaises(DecodeError):
3028             ObjectIdentifier().decode(b"".join((
3029                 Integer.tag_default,
3030                 len_encode(len(data)),
3031                 data,
3032             )))
3033
3034     def test_x690_vector(self):
3035         self.assertEqual(
3036             ObjectIdentifier().decode(hexdec("0603883703"))[0],
3037             ObjectIdentifier((2, 999, 3)),
3038         )
3039
3040     def test_nonnormalized_first_arc(self):
3041         tampered = (
3042             ObjectIdentifier.tag_default +
3043             len_encode(2) +
3044             b'\x80' +
3045             ObjectIdentifier((1, 0)).encode()[-1:]
3046         )
3047         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
3048         self.assertTrue(obj.ber_encoded)
3049         self.assertTrue(obj.bered)
3050         obj = copy(obj)
3051         self.assertTrue(obj.ber_encoded)
3052         self.assertTrue(obj.bered)
3053         with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
3054             ObjectIdentifier().decode(tampered)
3055
3056     @given(data_strategy())
3057     def test_negative_arcs(self, d):
3058         oid = list(d.draw(oid_strategy()))
3059         if len(oid) == 2:
3060             return
3061         idx = d.draw(integers(min_value=3, max_value=len(oid)))
3062         oid[idx - 1] *= -1
3063         if oid[idx - 1] == 0:
3064             oid[idx - 1] = -1
3065         with self.assertRaises(InvalidOID):
3066             ObjectIdentifier(tuple(oid))
3067         with self.assertRaises(InvalidOID):
3068             ObjectIdentifier(".".join(str(i) for i in oid))
3069
3070     @given(data_strategy())
3071     def test_plused_arcs(self, d):
3072         oid = [str(arc) for arc in d.draw(oid_strategy())]
3073         idx = d.draw(integers(min_value=0, max_value=len(oid)))
3074         oid[idx - 1] = "+" + oid[idx - 1]
3075         with self.assertRaises(InvalidOID):
3076             ObjectIdentifier(".".join(str(i) for i in oid))
3077
3078     @given(data_strategy())
3079     def test_nonnormalized_arcs(self, d):
3080         arcs = d.draw(lists(
3081             integers(min_value=0, max_value=100),
3082             min_size=1,
3083             max_size=5,
3084         ))
3085         dered = ObjectIdentifier((1, 0) + tuple(arcs)).encode()
3086         _, _, lv = tag_strip(dered)
3087         _, _, v = len_decode(lv)
3088         v_no_first_arc = v[1:]
3089         idx_for_tamper = d.draw(integers(
3090             min_value=0,
3091             max_value=len(v_no_first_arc) - 1,
3092         ))
3093         tampered = list(bytearray(v_no_first_arc))
3094         for _ in range(d.draw(integers(min_value=1, max_value=3))):
3095             tampered.insert(idx_for_tamper, 0x80)
3096         tampered = bytes(bytearray(tampered))
3097         tampered = (
3098             ObjectIdentifier.tag_default +
3099             len_encode(len(tampered)) +
3100             tampered
3101         )
3102         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
3103         self.assertTrue(obj.ber_encoded)
3104         self.assertTrue(obj.bered)
3105         obj = copy(obj)
3106         self.assertTrue(obj.ber_encoded)
3107         self.assertTrue(obj.bered)
3108         with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
3109             ObjectIdentifier().decode(tampered)
3110
3111
3112 @composite
3113 def enumerated_values_strategy(draw, schema=None, do_expl=False):
3114     if schema is None:
3115         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
3116         values = list(draw(sets(
3117             integers(),
3118             min_size=len(schema),
3119             max_size=len(schema),
3120         )))
3121         schema = list(zip(schema, values))
3122     value = draw(one_of(none(), sampled_from([k for k, v in schema])))
3123     impl = None
3124     expl = None
3125     if do_expl:
3126         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3127     else:
3128         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3129     default = draw(one_of(none(), sampled_from([v for k, v in schema])))
3130     optional = draw(one_of(none(), booleans()))
3131     _decoded = (
3132         draw(integers(min_value=0)),
3133         draw(integers(min_value=0)),
3134         draw(integers(min_value=0)),
3135     )
3136     return (schema, value, impl, expl, default, optional, _decoded)
3137
3138
3139 class TestEnumerated(CommonMixin, TestCase):
3140     class EWhatever(Enumerated):
3141         schema = (("whatever", 0),)
3142
3143     base_klass = EWhatever
3144
3145     def test_schema_required(self):
3146         with assertRaisesRegex(self, ValueError, "schema must be specified"):
3147             Enumerated()
3148
3149     def test_invalid_value_type(self):
3150         with self.assertRaises(InvalidValueType) as err:
3151             self.base_klass((1, 2))
3152         repr(err.exception)
3153
3154     @given(sets(text_letters(), min_size=2))
3155     def test_unknown_name(self, schema_input):
3156         missing = schema_input.pop()
3157
3158         class E(Enumerated):
3159             schema = [(n, 123) for n in schema_input]
3160         with self.assertRaises(ObjUnknown) as err:
3161             E(missing)
3162         repr(err.exception)
3163
3164     @given(
3165         sets(text_letters(), min_size=2),
3166         sets(integers(), min_size=2),
3167     )
3168     def test_unknown_value(self, schema_input, values_input):
3169         schema_input.pop()
3170         missing_value = values_input.pop()
3171         _input = list(zip(schema_input, values_input))
3172
3173         class E(Enumerated):
3174             schema = _input
3175         with self.assertRaises(DecodeError) as err:
3176             E(missing_value)
3177         repr(err.exception)
3178
3179     @given(booleans())
3180     def test_optional(self, optional):
3181         obj = self.base_klass(default="whatever", optional=optional)
3182         self.assertTrue(obj.optional)
3183
3184     def test_ready(self):
3185         obj = self.base_klass()
3186         self.assertFalse(obj.ready)
3187         repr(obj)
3188         list(obj.pps())
3189         pprint(obj, big_blobs=True, with_decode_path=True)
3190         with self.assertRaises(ObjNotReady) as err:
3191             obj.encode()
3192         repr(err.exception)
3193         obj = self.base_klass("whatever")
3194         self.assertTrue(obj.ready)
3195         repr(obj)
3196         list(obj.pps())
3197         pprint(obj, big_blobs=True, with_decode_path=True)
3198
3199     @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
3200     def test_comparison(self, value1, value2, tag1, tag2):
3201         class E(Enumerated):
3202             schema = (
3203                 ("whatever0", value1),
3204                 ("whatever1", value2),
3205             )
3206
3207         class EInherited(E):
3208             pass
3209         for klass in (E, EInherited):
3210             obj1 = klass(value1)
3211             obj2 = klass(value2)
3212             self.assertEqual(obj1 == obj2, value1 == value2)
3213             self.assertEqual(obj1 != obj2, value1 != value2)
3214             self.assertEqual(obj1 == int(obj2), value1 == value2)
3215             obj1 = klass(value1, impl=tag1)
3216             obj2 = klass(value1, impl=tag2)
3217             self.assertEqual(obj1 == obj2, tag1 == tag2)
3218             self.assertEqual(obj1 != obj2, tag1 != tag2)
3219
3220     @given(data_strategy())
3221     def test_call(self, d):
3222         (
3223             schema_initial,
3224             value_initial,
3225             impl_initial,
3226             expl_initial,
3227             default_initial,
3228             optional_initial,
3229             _decoded_initial,
3230         ) = d.draw(enumerated_values_strategy())
3231
3232         class E(Enumerated):
3233             schema = schema_initial
3234         obj_initial = E(
3235             value=value_initial,
3236             impl=impl_initial,
3237             expl=expl_initial,
3238             default=default_initial,
3239             optional=optional_initial or False,
3240             _decoded=_decoded_initial,
3241         )
3242         (
3243             _,
3244             value,
3245             impl,
3246             expl,
3247             default,
3248             optional,
3249             _decoded,
3250         ) = d.draw(enumerated_values_strategy(
3251             schema=schema_initial,
3252             do_expl=impl_initial is None,
3253         ))
3254         obj = obj_initial(
3255             value=value,
3256             impl=impl,
3257             expl=expl,
3258             default=default,
3259             optional=optional,
3260         )
3261         if obj.ready:
3262             value_expected = default if value is None else value
3263             value_expected = (
3264                 default_initial if value_expected is None
3265                 else value_expected
3266             )
3267             self.assertEqual(
3268                 int(obj),
3269                 dict(schema_initial).get(value_expected, value_expected),
3270             )
3271         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3272         self.assertEqual(obj.expl_tag, expl or expl_initial)
3273         self.assertEqual(
3274             obj.default,
3275             default_initial if default is None else default,
3276         )
3277         if obj.default is None:
3278             optional = optional_initial if optional is None else optional
3279             optional = False if optional is None else optional
3280         else:
3281             optional = True
3282         self.assertEqual(obj.optional, optional)
3283         self.assertEqual(obj.specs, dict(schema_initial))
3284
3285     @given(enumerated_values_strategy())
3286     def test_copy(self, values):
3287         schema_input, value, impl, expl, default, optional, _decoded = values
3288
3289         class E(Enumerated):
3290             schema = schema_input
3291         register_class(E)
3292         obj = E(
3293             value=value,
3294             impl=impl,
3295             expl=expl,
3296             default=default,
3297             optional=optional,
3298             _decoded=_decoded,
3299         )
3300         for copy_func in copy_funcs:
3301             obj_copied = copy_func(obj)
3302             self.assert_copied_basic_fields(obj, obj_copied)
3303             self.assertEqual(obj.specs, obj_copied.specs)
3304
3305     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3306     @given(data_strategy())
3307     def test_symmetric(self, d):
3308         schema_input, _, _, _, default, optional, _decoded = d.draw(
3309             enumerated_values_strategy(),
3310         )
3311         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
3312         offset = d.draw(integers(min_value=0))
3313         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
3314         tail_junk = d.draw(binary(max_size=5))
3315         decode_path = d.draw(decode_path_strat)
3316
3317         class E(Enumerated):
3318             schema = schema_input
3319         obj = E(
3320             value=value,
3321             default=default,
3322             optional=optional,
3323             _decoded=_decoded,
3324         )
3325         repr(obj)
3326         list(obj.pps())
3327         pprint(obj, big_blobs=True, with_decode_path=True)
3328         self.assertFalse(obj.expled)
3329         obj_encoded = obj.encode()
3330         obj_expled = obj(value, expl=tag_expl)
3331         self.assertTrue(obj_expled.expled)
3332         repr(obj_expled)
3333         list(obj_expled.pps())
3334         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3335         obj_expled_encoded = obj_expled.encode()
3336         ctx_copied = deepcopy(ctx_dummy)
3337         obj_decoded, tail = obj_expled.decode(
3338             obj_expled_encoded + tail_junk,
3339             offset=offset,
3340             ctx=ctx_copied,
3341         )
3342         self.assertDictEqual(ctx_copied, ctx_dummy)
3343         repr(obj_decoded)
3344         list(obj_decoded.pps())
3345         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3346         self.assertEqual(tail, tail_junk)
3347         self.assertEqual(obj_decoded, obj_expled)
3348         self.assertNotEqual(obj_decoded, obj)
3349         self.assertEqual(int(obj_decoded), int(obj_expled))
3350         self.assertEqual(int(obj_decoded), int(obj))
3351         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3352         self.assertEqual(obj_decoded.expl_tag, tag_expl)
3353         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3354         self.assertEqual(
3355             obj_decoded.expl_llen,
3356             len(len_encode(len(obj_encoded))),
3357         )
3358         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3359         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3360         self.assertEqual(
3361             obj_decoded.offset,
3362             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3363         )
3364         self.assertEqual(obj_decoded.expl_offset, offset)
3365         assert_exceeding_data(
3366             self,
3367             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3368             tail_junk,
3369         )
3370
3371         evgens = list(obj_expled.decode_evgen(
3372             obj_expled_encoded + tail_junk,
3373             offset=offset,
3374             decode_path=decode_path,
3375             ctx=ctx_copied,
3376         ))
3377         self.assertEqual(len(evgens), 1)
3378         _decode_path, obj, tail = evgens[0]
3379         self.assertSequenceEqual(tail, tail_junk)
3380         self.assertEqual(_decode_path, decode_path)
3381         self.assertEqual(obj, obj_decoded)
3382         self.assertEqual(obj.expl_offset, offset)
3383         repr(obj)
3384         list(obj.pps())
3385
3386
3387 @composite
3388 def string_values_strategy(draw, alphabet, do_expl=False):
3389     bound_min, bound_max = sorted(draw(sets(
3390         integers(min_value=0, max_value=1 << 7),
3391         min_size=2,
3392         max_size=2,
3393     )))
3394     value = draw(one_of(
3395         none(),
3396         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3397     ))
3398     default = draw(one_of(
3399         none(),
3400         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3401     ))
3402     bounds = None
3403     if draw(booleans()):
3404         bounds = (bound_min, bound_max)
3405     impl = None
3406     expl = None
3407     if do_expl:
3408         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3409     else:
3410         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3411     optional = draw(one_of(none(), booleans()))
3412     _decoded = (
3413         draw(integers(min_value=0)),
3414         draw(integers(min_value=0)),
3415         draw(integers(min_value=0)),
3416     )
3417     return (value, bounds, impl, expl, default, optional, _decoded)
3418
3419
3420 class StringMixin(object):
3421     def test_invalid_value_type(self):
3422         with self.assertRaises(InvalidValueType) as err:
3423             self.base_klass((1, 2))
3424         repr(err.exception)
3425
3426     def text_alphabet(self):
3427         if self.base_klass.encoding in ("ascii", "iso-8859-1"):
3428             return printable + whitespace
3429         return None
3430
3431     @given(booleans())
3432     def test_optional(self, optional):
3433         obj = self.base_klass(default=self.base_klass(""), optional=optional)
3434         self.assertTrue(obj.optional)
3435
3436     @given(data_strategy())
3437     def test_ready(self, d):
3438         obj = self.base_klass()
3439         self.assertFalse(obj.ready)
3440         repr(obj)
3441         list(obj.pps())
3442         pprint(obj, big_blobs=True, with_decode_path=True)
3443         text_type(obj)
3444         with self.assertRaises(ObjNotReady) as err:
3445             obj.encode()
3446         repr(err.exception)
3447         value = d.draw(text(alphabet=self.text_alphabet()))
3448         obj = self.base_klass(value)
3449         self.assertTrue(obj.ready)
3450         repr(obj)
3451         list(obj.pps())
3452         pprint(obj, big_blobs=True, with_decode_path=True)
3453         text_type(obj)
3454
3455     @given(data_strategy())
3456     def test_comparison(self, d):
3457         value1 = d.draw(text(alphabet=self.text_alphabet()))
3458         value2 = d.draw(text(alphabet=self.text_alphabet()))
3459         tag1 = d.draw(binary(min_size=1))
3460         tag2 = d.draw(binary(min_size=1))
3461         obj1 = self.base_klass(value1)
3462         obj2 = self.base_klass(value2)
3463         self.assertEqual(obj1 == obj2, value1 == value2)
3464         self.assertEqual(obj1 != obj2, value1 != value2)
3465         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3466         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
3467         obj1 = self.base_klass(value1, impl=tag1)
3468         obj2 = self.base_klass(value1, impl=tag2)
3469         self.assertEqual(obj1 == obj2, tag1 == tag2)
3470         self.assertEqual(obj1 != obj2, tag1 != tag2)
3471
3472     @given(data_strategy())
3473     def test_bounds_satisfied(self, d):
3474         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
3475         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3476         value = d.draw(text(
3477             alphabet=self.text_alphabet(),
3478             min_size=bound_min,
3479             max_size=bound_max,
3480         ))
3481         self.base_klass(value=value, bounds=(bound_min, bound_max))
3482
3483     @given(data_strategy())
3484     def test_bounds_unsatisfied(self, d):
3485         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
3486         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3487         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
3488         with self.assertRaises(BoundsError) as err:
3489             self.base_klass(value=value, bounds=(bound_min, bound_max))
3490         repr(err.exception)
3491         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3492             self.base_klass(bounds=(bound_min, bound_max)).decode(
3493                 self.base_klass(value).encode()
3494             )
3495         repr(err.exception)
3496         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
3497         with self.assertRaises(BoundsError) as err:
3498             self.base_klass(value=value, bounds=(bound_min, bound_max))
3499         repr(err.exception)
3500         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3501             self.base_klass(bounds=(bound_min, bound_max)).decode(
3502                 self.base_klass(value).encode()
3503             )
3504         repr(err.exception)
3505
3506     @given(data_strategy())
3507     def test_call(self, d):
3508         (
3509             value_initial,
3510             bounds_initial,
3511             impl_initial,
3512             expl_initial,
3513             default_initial,
3514             optional_initial,
3515             _decoded_initial,
3516         ) = d.draw(string_values_strategy(self.text_alphabet()))
3517         obj_initial = self.base_klass(
3518             value_initial,
3519             bounds_initial,
3520             impl_initial,
3521             expl_initial,
3522             default_initial,
3523             optional_initial or False,
3524             _decoded_initial,
3525         )
3526         (
3527             value,
3528             bounds,
3529             impl,
3530             expl,
3531             default,
3532             optional,
3533             _decoded,
3534         ) = d.draw(string_values_strategy(
3535             self.text_alphabet(),
3536             do_expl=impl_initial is None,
3537         ))
3538         if (default is None) and (obj_initial.default is not None):
3539             bounds = None
3540         if (
3541                 (bounds is None) and
3542                 (value is not None) and
3543                 (bounds_initial is not None) and
3544                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
3545         ):
3546             value = None
3547         if (
3548                 (bounds is None) and
3549                 (default is not None) and
3550                 (bounds_initial is not None) and
3551                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
3552         ):
3553             default = None
3554         obj = obj_initial(value, bounds, impl, expl, default, optional)
3555         if obj.ready:
3556             value_expected = default if value is None else value
3557             value_expected = (
3558                 default_initial if value_expected is None
3559                 else value_expected
3560             )
3561             self.assertEqual(obj, value_expected)
3562         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3563         self.assertEqual(obj.expl_tag, expl or expl_initial)
3564         self.assertEqual(
3565             obj.default,
3566             default_initial if default is None else default,
3567         )
3568         if obj.default is None:
3569             optional = optional_initial if optional is None else optional
3570             optional = False if optional is None else optional
3571         else:
3572             optional = True
3573         self.assertEqual(obj.optional, optional)
3574         self.assertEqual(
3575             (obj._bound_min, obj._bound_max),
3576             bounds or bounds_initial or (0, float("+inf")),
3577         )
3578
3579     @given(data_strategy())
3580     def test_copy(self, d):
3581         values = d.draw(string_values_strategy(self.text_alphabet()))
3582         obj = self.base_klass(*values)
3583         for copy_func in copy_funcs:
3584             obj_copied = copy_func(obj)
3585             self.assert_copied_basic_fields(obj, obj_copied)
3586             self.assertEqual(obj._bound_min, obj_copied._bound_min)
3587             self.assertEqual(obj._bound_max, obj_copied._bound_max)
3588             self.assertEqual(obj._value, obj_copied._value)
3589
3590     @given(data_strategy())
3591     def test_stripped(self, d):
3592         value = d.draw(text(alphabet=self.text_alphabet()))
3593         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3594         obj = self.base_klass(value, impl=tag_impl)
3595         with self.assertRaises(NotEnoughData):
3596             obj.decode(obj.encode()[:-1])
3597
3598     @given(data_strategy())
3599     def test_stripped_expl(self, d):
3600         value = d.draw(text(alphabet=self.text_alphabet()))
3601         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3602         obj = self.base_klass(value, expl=tag_expl)
3603         with self.assertRaises(NotEnoughData):
3604             obj.decode(obj.encode()[:-1])
3605
3606     @given(
3607         integers(min_value=31),
3608         integers(min_value=0),
3609         decode_path_strat,
3610     )
3611     def test_bad_tag(self, tag, offset, decode_path):
3612         with self.assertRaises(DecodeError) as err:
3613             self.base_klass().decode(
3614                 tag_encode(tag)[:-1],
3615                 offset=offset,
3616                 decode_path=decode_path,
3617             )
3618         repr(err.exception)
3619         self.assertEqual(err.exception.offset, offset)
3620         self.assertEqual(err.exception.decode_path, decode_path)
3621
3622     @given(
3623         integers(min_value=128),
3624         integers(min_value=0),
3625         decode_path_strat,
3626     )
3627     def test_bad_len(self, l, offset, decode_path):
3628         with self.assertRaises(DecodeError) as err:
3629             self.base_klass().decode(
3630                 self.base_klass.tag_default + len_encode(l)[:-1],
3631                 offset=offset,
3632                 decode_path=decode_path,
3633             )
3634         repr(err.exception)
3635         self.assertEqual(err.exception.offset, offset)
3636         self.assertEqual(err.exception.decode_path, decode_path)
3637
3638     @given(
3639         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3640         integers(min_value=0),
3641         decode_path_strat,
3642     )
3643     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3644         value, bound_min = list(sorted(ints))
3645
3646         class String(self.base_klass):
3647             # Multiply this value by four, to satisfy UTF-32 bounds
3648             # (4 bytes per character) validation
3649             bounds = (bound_min * 4, bound_min * 4)
3650         with self.assertRaises(DecodeError) as err:
3651             String().decode(
3652                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
3653                 offset=offset,
3654                 decode_path=decode_path,
3655             )
3656         repr(err.exception)
3657         self.assertEqual(err.exception.offset, offset)
3658         self.assertEqual(err.exception.decode_path, decode_path)
3659
3660     @given(data_strategy())
3661     def test_symmetric(self, d):
3662         values = d.draw(string_values_strategy(self.text_alphabet()))
3663         value = d.draw(text(alphabet=self.text_alphabet()))
3664         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3665         offset = d.draw(integers(min_value=0))
3666         tail_junk = d.draw(binary(max_size=5))
3667         decode_path = d.draw(decode_path_strat)
3668         _, _, _, _, default, optional, _decoded = values
3669         obj = self.base_klass(
3670             value=value,
3671             default=default,
3672             optional=optional,
3673             _decoded=_decoded,
3674         )
3675         repr(obj)
3676         list(obj.pps())
3677         pprint(obj, big_blobs=True, with_decode_path=True)
3678         self.assertFalse(obj.expled)
3679         obj_encoded = obj.encode()
3680         obj_expled = obj(value, expl=tag_expl)
3681         self.assertTrue(obj_expled.expled)
3682         repr(obj_expled)
3683         list(obj_expled.pps())
3684         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3685         obj_expled_encoded = obj_expled.encode()
3686         ctx_copied = deepcopy(ctx_dummy)
3687         obj_decoded, tail = obj_expled.decode(
3688             obj_expled_encoded + tail_junk,
3689             offset=offset,
3690             ctx=ctx_copied,
3691         )
3692         self.assertDictEqual(ctx_copied, ctx_dummy)
3693         repr(obj_decoded)
3694         list(obj_decoded.pps())
3695         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3696         self.assertEqual(tail, tail_junk)
3697         self.assertEqual(obj_decoded, obj_expled)
3698         self.assertNotEqual(obj_decoded, obj)
3699         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3700         self.assertEqual(bytes(obj_decoded), bytes(obj))
3701         self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
3702         self.assertEqual(text_type(obj_decoded), text_type(obj))
3703         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3704         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3705         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3706         self.assertEqual(
3707             obj_decoded.expl_llen,
3708             len(len_encode(len(obj_encoded))),
3709         )
3710         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3711         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3712         self.assertEqual(
3713             obj_decoded.offset,
3714             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3715         )
3716         self.assertEqual(obj_decoded.expl_offset, offset)
3717         assert_exceeding_data(
3718             self,
3719             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3720             tail_junk,
3721         )
3722
3723         evgens = list(obj_expled.decode_evgen(
3724             obj_expled_encoded + tail_junk,
3725             offset=offset,
3726             decode_path=decode_path,
3727             ctx=ctx_copied,
3728         ))
3729         self.assertEqual(len(evgens), 1)
3730         _decode_path, obj, tail = evgens[0]
3731         self.assertSequenceEqual(tail, tail_junk)
3732         self.assertEqual(_decode_path, decode_path)
3733         if not getattr(self, "evgen_mode_skip_value", True):
3734             self.assertEqual(obj, obj_decoded)
3735         self.assertEqual(obj.expl_offset, offset)
3736         repr(obj)
3737         list(obj.pps())
3738
3739
3740 class TestUTF8String(StringMixin, CommonMixin, TestCase):
3741     base_klass = UTF8String
3742
3743
3744 cyrillic_letters = text(
3745     alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
3746     min_size=1,
3747     max_size=5,
3748 )
3749
3750
3751 class UnicodeDecodeErrorMixin(object):
3752     @given(cyrillic_letters)
3753     def test_unicode_decode_error(self, cyrillic_text):
3754         with self.assertRaises(DecodeError):
3755             self.base_klass(cyrillic_text)
3756
3757
3758 class TestNumericString(StringMixin, CommonMixin, TestCase):
3759     base_klass = NumericString
3760
3761     def text_alphabet(self):
3762         return digits + " "
3763
3764     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
3765     def test_non_numeric(self, non_numeric_text):
3766         with assertRaisesRegex(self, DecodeError, "non-numeric"):
3767             self.base_klass(non_numeric_text)
3768
3769     @given(
3770         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3771         integers(min_value=0),
3772         decode_path_strat,
3773     )
3774     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3775         value, bound_min = list(sorted(ints))
3776
3777         class String(self.base_klass):
3778             bounds = (bound_min, bound_min)
3779         with self.assertRaises(DecodeError) as err:
3780             String().decode(
3781                 self.base_klass(b"1" * value).encode(),
3782                 offset=offset,
3783                 decode_path=decode_path,
3784             )
3785         repr(err.exception)
3786         self.assertEqual(err.exception.offset, offset)
3787         self.assertEqual(err.exception.decode_path, decode_path)
3788
3789
3790 class TestPrintableString(
3791         UnicodeDecodeErrorMixin,
3792         StringMixin,
3793         CommonMixin,
3794         TestCase,
3795 ):
3796     base_klass = PrintableString
3797
3798     def text_alphabet(self):
3799         return ascii_letters + digits + " '()+,-./:=?"
3800
3801     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
3802     def test_non_printable(self, non_printable_text):
3803         with assertRaisesRegex(self, DecodeError, "non-printable"):
3804             self.base_klass(non_printable_text)
3805
3806     @given(
3807         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3808         integers(min_value=0),
3809         decode_path_strat,
3810     )
3811     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3812         value, bound_min = list(sorted(ints))
3813
3814         class String(self.base_klass):
3815             bounds = (bound_min, bound_min)
3816         with self.assertRaises(DecodeError) as err:
3817             String().decode(
3818                 self.base_klass(b"1" * value).encode(),
3819                 offset=offset,
3820                 decode_path=decode_path,
3821             )
3822         repr(err.exception)
3823         self.assertEqual(err.exception.offset, offset)
3824         self.assertEqual(err.exception.decode_path, decode_path)
3825
3826     def test_allowable_invalid_chars(self):
3827         for c, kwargs in (
3828                 ("*", {"allow_asterisk": True}),
3829                 ("&", {"allow_ampersand": True}),
3830                 ("&*", {"allow_asterisk": True, "allow_ampersand": True}),
3831         ):
3832             s = "hello invalid"
3833             obj = self.base_klass(s)
3834             for prop in kwargs.keys():
3835                 self.assertFalse(getattr(obj, prop))
3836             s += c
3837             with assertRaisesRegex(self, DecodeError, "non-printable"):
3838                 self.base_klass(s)
3839             self.base_klass(s, **kwargs)
3840             klass = self.base_klass(**kwargs)
3841             obj = klass(s)
3842             for prop in kwargs.keys():
3843                 self.assertTrue(getattr(obj, prop))
3844             obj = copy(obj)
3845             obj(s)
3846             for prop in kwargs.keys():
3847                 self.assertTrue(getattr(obj, prop))
3848
3849
3850 class TestTeletexString(
3851         UnicodeDecodeErrorMixin,
3852         StringMixin,
3853         CommonMixin,
3854         TestCase,
3855 ):
3856     base_klass = TeletexString
3857
3858
3859 class TestVideotexString(
3860         UnicodeDecodeErrorMixin,
3861         StringMixin,
3862         CommonMixin,
3863         TestCase,
3864 ):
3865     base_klass = VideotexString
3866
3867
3868 class TestIA5String(
3869         UnicodeDecodeErrorMixin,
3870         StringMixin,
3871         CommonMixin,
3872         TestCase,
3873 ):
3874     base_klass = IA5String
3875
3876
3877 class TestGraphicString(
3878         UnicodeDecodeErrorMixin,
3879         StringMixin,
3880         CommonMixin,
3881         TestCase,
3882 ):
3883     base_klass = GraphicString
3884
3885
3886 class TestVisibleString(
3887         UnicodeDecodeErrorMixin,
3888         StringMixin,
3889         CommonMixin,
3890         TestCase,
3891 ):
3892     base_klass = VisibleString
3893
3894     def test_x690_vector(self):
3895         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3896         self.assertSequenceEqual(tail, b"")
3897         self.assertEqual(str(obj), "Jones")
3898         self.assertFalse(obj.ber_encoded)
3899         self.assertFalse(obj.lenindef)
3900         self.assertFalse(obj.bered)
3901
3902         obj, tail = VisibleString().decode(
3903             hexdec("3A0904034A6F6E04026573"),
3904             ctx={"bered": True},
3905         )
3906         self.assertSequenceEqual(tail, b"")
3907         self.assertEqual(str(obj), "Jones")
3908         self.assertTrue(obj.ber_encoded)
3909         self.assertFalse(obj.lenindef)
3910         self.assertTrue(obj.bered)
3911         obj = copy(obj)
3912         self.assertTrue(obj.ber_encoded)
3913         self.assertFalse(obj.lenindef)
3914         self.assertTrue(obj.bered)
3915
3916         obj, tail = VisibleString().decode(
3917             hexdec("3A8004034A6F6E040265730000"),
3918             ctx={"bered": True},
3919         )
3920         self.assertSequenceEqual(tail, b"")
3921         self.assertEqual(str(obj), "Jones")
3922         self.assertTrue(obj.ber_encoded)
3923         self.assertTrue(obj.lenindef)
3924         self.assertTrue(obj.bered)
3925         obj = copy(obj)
3926         self.assertTrue(obj.ber_encoded)
3927         self.assertTrue(obj.lenindef)
3928         self.assertTrue(obj.bered)
3929
3930
3931 class TestGeneralString(
3932         UnicodeDecodeErrorMixin,
3933         StringMixin,
3934         CommonMixin,
3935         TestCase,
3936 ):
3937     base_klass = GeneralString
3938
3939
3940 class TestUniversalString(StringMixin, CommonMixin, TestCase):
3941     base_klass = UniversalString
3942
3943
3944 class TestBMPString(StringMixin, CommonMixin, TestCase):
3945     base_klass = BMPString
3946
3947
3948 @composite
3949 def generalized_time_values_strategy(
3950         draw,
3951         min_datetime,
3952         max_datetime,
3953         omit_ms=False,
3954         do_expl=False,
3955 ):
3956     value = None
3957     if draw(booleans()):
3958         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3959         if omit_ms:
3960             value = value.replace(microsecond=0)
3961     default = None
3962     if draw(booleans()):
3963         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3964         if omit_ms:
3965             default = default.replace(microsecond=0)
3966     impl = None
3967     expl = None
3968     if do_expl:
3969         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3970     else:
3971         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3972     optional = draw(one_of(none(), booleans()))
3973     _decoded = (
3974         draw(integers(min_value=0)),
3975         draw(integers(min_value=0)),
3976         draw(integers(min_value=0)),
3977     )
3978     return (value, impl, expl, default, optional, _decoded)
3979
3980
3981 class TimeMixin(object):
3982     def test_invalid_value_type(self):
3983         with self.assertRaises(InvalidValueType) as err:
3984             self.base_klass(datetime.now().timetuple())
3985         repr(err.exception)
3986
3987     @given(data_strategy())
3988     def test_optional(self, d):
3989         default = d.draw(datetimes(
3990             min_value=self.min_datetime,
3991             max_value=self.max_datetime,
3992         ))
3993         optional = d.draw(booleans())
3994         obj = self.base_klass(default=default, optional=optional)
3995         self.assertTrue(obj.optional)
3996
3997     @given(data_strategy())
3998     def test_ready(self, d):
3999         obj = self.base_klass()
4000         self.assertFalse(obj.ready)
4001         repr(obj)
4002         list(obj.pps())
4003         pprint(obj, big_blobs=True, with_decode_path=True)
4004         with self.assertRaises(ObjNotReady) as err:
4005             obj.encode()
4006         repr(err.exception)
4007         value = d.draw(datetimes(
4008             min_value=self.min_datetime,
4009             max_value=self.max_datetime,
4010         ))
4011         obj = self.base_klass(value)
4012         self.assertTrue(obj.ready)
4013         repr(obj)
4014         list(obj.pps())
4015         pprint(obj, big_blobs=True, with_decode_path=True)
4016
4017     @given(data_strategy())
4018     def test_comparison(self, d):
4019         value1 = d.draw(datetimes(
4020             min_value=self.min_datetime,
4021             max_value=self.max_datetime,
4022         ))
4023         value2 = d.draw(datetimes(
4024             min_value=self.min_datetime,
4025             max_value=self.max_datetime,
4026         ))
4027         tag1 = d.draw(binary(min_size=1))
4028         tag2 = d.draw(binary(min_size=1))
4029         if self.omit_ms:
4030             value1 = value1.replace(microsecond=0)
4031             value2 = value2.replace(microsecond=0)
4032         obj1 = self.base_klass(value1)
4033         obj2 = self.base_klass(value2)
4034         self.assertEqual(obj1 == obj2, value1 == value2)
4035         self.assertEqual(obj1 != obj2, value1 != value2)
4036         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
4037         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
4038         obj1 = self.base_klass(value1, impl=tag1)
4039         obj2 = self.base_klass(value1, impl=tag2)
4040         self.assertEqual(obj1 == obj2, tag1 == tag2)
4041         self.assertEqual(obj1 != obj2, tag1 != tag2)
4042
4043     @given(data_strategy())
4044     def test_call(self, d):
4045         (
4046             value_initial,
4047             impl_initial,
4048             expl_initial,
4049             default_initial,
4050             optional_initial,
4051             _decoded_initial,
4052         ) = d.draw(generalized_time_values_strategy(
4053             min_datetime=self.min_datetime,
4054             max_datetime=self.max_datetime,
4055             omit_ms=self.omit_ms,
4056         ))
4057         obj_initial = self.base_klass(
4058             value=value_initial,
4059             impl=impl_initial,
4060             expl=expl_initial,
4061             default=default_initial,
4062             optional=optional_initial or False,
4063             _decoded=_decoded_initial,
4064         )
4065         (
4066             value,
4067             impl,
4068             expl,
4069             default,
4070             optional,
4071             _decoded,
4072         ) = d.draw(generalized_time_values_strategy(
4073             min_datetime=self.min_datetime,
4074             max_datetime=self.max_datetime,
4075             omit_ms=self.omit_ms,
4076             do_expl=impl_initial is None,
4077         ))
4078         obj = obj_initial(
4079             value=value,
4080             impl=impl,
4081             expl=expl,
4082             default=default,
4083             optional=optional,
4084         )
4085         if obj.ready:
4086             value_expected = default if value is None else value
4087             value_expected = (
4088                 default_initial if value_expected is None
4089                 else value_expected
4090             )
4091             self.assertEqual(obj, value_expected)
4092         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4093         self.assertEqual(obj.expl_tag, expl or expl_initial)
4094         self.assertEqual(
4095             obj.default,
4096             default_initial if default is None else default,
4097         )
4098         if obj.default is None:
4099             optional = optional_initial if optional is None else optional
4100             optional = False if optional is None else optional
4101         else:
4102             optional = True
4103         self.assertEqual(obj.optional, optional)
4104
4105     @given(data_strategy())
4106     def test_copy(self, d):
4107         values = d.draw(generalized_time_values_strategy(
4108             min_datetime=self.min_datetime,
4109             max_datetime=self.max_datetime,
4110         ))
4111         obj = self.base_klass(*values)
4112         for copy_func in copy_funcs:
4113             obj_copied = copy_func(obj)
4114             self.assert_copied_basic_fields(obj, obj_copied)
4115             self.assertEqual(obj._value, obj_copied._value)
4116
4117     @given(data_strategy())
4118     def test_stripped(self, d):
4119         value = d.draw(datetimes(
4120             min_value=self.min_datetime,
4121             max_value=self.max_datetime,
4122         ))
4123         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4124         obj = self.base_klass(value, impl=tag_impl)
4125         with self.assertRaises(NotEnoughData):
4126             obj.decode(obj.encode()[:-1])
4127
4128     @given(data_strategy())
4129     def test_stripped_expl(self, d):
4130         value = d.draw(datetimes(
4131             min_value=self.min_datetime,
4132             max_value=self.max_datetime,
4133         ))
4134         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4135         obj = self.base_klass(value, expl=tag_expl)
4136         with self.assertRaises(NotEnoughData):
4137             obj.decode(obj.encode()[:-1])
4138
4139     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4140     @given(data_strategy())
4141     def test_symmetric(self, d):
4142         values = d.draw(generalized_time_values_strategy(
4143             min_datetime=self.min_datetime,
4144             max_datetime=self.max_datetime,
4145         ))
4146         value = d.draw(datetimes(
4147             min_value=self.min_datetime,
4148             max_value=self.max_datetime,
4149         ))
4150         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4151         offset = d.draw(integers(min_value=0))
4152         tail_junk = d.draw(binary(max_size=5))
4153         _, _, _, default, optional, _decoded = values
4154         obj = self.base_klass(
4155             value=value,
4156             default=default,
4157             optional=optional,
4158             _decoded=_decoded,
4159         )
4160         repr(obj)
4161         list(obj.pps())
4162         pprint(obj, big_blobs=True, with_decode_path=True)
4163         self.assertFalse(obj.expled)
4164         obj_encoded = obj.encode()
4165         self.additional_symmetric_check(value, obj_encoded)
4166         obj_expled = obj(value, expl=tag_expl)
4167         self.assertTrue(obj_expled.expled)
4168         repr(obj_expled)
4169         list(obj_expled.pps())
4170         pprint(obj_expled, big_blobs=True, with_decode_path=True)
4171         obj_expled_encoded = obj_expled.encode()
4172         ctx_copied = deepcopy(ctx_dummy)
4173         obj_decoded, tail = obj_expled.decode(
4174             obj_expled_encoded + tail_junk,
4175             offset=offset,
4176             ctx=ctx_copied,
4177         )
4178         self.assertDictEqual(ctx_copied, ctx_dummy)
4179         repr(obj_decoded)
4180         list(obj_decoded.pps())
4181         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4182         self.assertEqual(tail, tail_junk)
4183         self.assertEqual(obj_decoded, obj_expled)
4184         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
4185         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
4186         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4187         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4188         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4189         self.assertEqual(
4190             obj_decoded.expl_llen,
4191             len(len_encode(len(obj_encoded))),
4192         )
4193         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4194         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4195         self.assertEqual(
4196             obj_decoded.offset,
4197             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4198         )
4199         self.assertEqual(obj_decoded.expl_offset, offset)
4200         assert_exceeding_data(
4201             self,
4202             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
4203             tail_junk,
4204         )
4205
4206
4207 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
4208     base_klass = GeneralizedTime
4209     omit_ms = False
4210     min_datetime = datetime(1900, 1, 1)
4211     max_datetime = datetime(9999, 12, 31)
4212     evgen_mode_skip_value = False
4213
4214     def additional_symmetric_check(self, value, obj_encoded):
4215         if value.microsecond > 0:
4216             self.assertFalse(obj_encoded.endswith(b"0Z"))
4217
4218     def test_repr_not_ready(self):
4219         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
4220         repr(GeneralizedTime())
4221
4222     def test_x690_vector_valid(self):
4223         for data in ((
4224                 b"19920521000000Z",
4225                 b"19920622123421Z",
4226                 b"19920722132100.3Z",
4227         )):
4228             GeneralizedTime(data)
4229
4230     def test_x690_vector_invalid(self):
4231         for data in ((
4232                 b"19920520240000Z",
4233                 b"19920622123421.0Z",
4234                 b"19920722132100.30Z",
4235         )):
4236             with self.assertRaises(DecodeError) as err:
4237                 GeneralizedTime(data)
4238             repr(err.exception)
4239
4240     def test_go_vectors_invalid(self):
4241         for data in ((
4242                 b"20100102030405",
4243                 b"00000100000000Z",
4244                 b"20101302030405Z",
4245                 b"20100002030405Z",
4246                 b"20100100030405Z",
4247                 b"20100132030405Z",
4248                 b"20100231030405Z",
4249                 b"20100102240405Z",
4250                 b"20100102036005Z",
4251                 b"20100102030460Z",
4252                 b"-20100102030410Z",
4253                 b"2010-0102030410Z",
4254                 b"2010-0002030410Z",
4255                 b"201001-02030410Z",
4256                 b"20100102-030410Z",
4257                 b"2010010203-0410Z",
4258                 b"201001020304-10Z",
4259                 # These ones are INVALID in *DER*, but accepted
4260                 # by Go's encoding/asn1
4261                 b"20100102030405+0607",
4262                 b"20100102030405-0607",
4263         )):
4264             with self.assertRaises(DecodeError) as err:
4265                 GeneralizedTime(data)
4266             repr(err.exception)
4267
4268     def test_go_vectors_valid(self):
4269         self.assertEqual(
4270             GeneralizedTime(b"20100102030405Z").todatetime(),
4271             datetime(2010, 1, 2, 3, 4, 5, 0),
4272         )
4273
4274     def test_go_vectors_valid_ber(self):
4275         for data in ((
4276                 b"20100102030405+0607",
4277                 b"20100102030405-0607",
4278         )):
4279             GeneralizedTime(data, ctx={"bered": True})
4280
4281     def test_utc_offsets(self):
4282         """Some know equal UTC offsets
4283         """
4284         dts = [
4285             GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4286             for data in (
4287                 "200101011830Z",
4288                 "200101012230+04",
4289                 "200101011130-0700",
4290                 "200101011500-03:30",
4291             )
4292         ]
4293         self.assertEqual(dts[0], dts[1])
4294         self.assertEqual(dts[0], dts[2])
4295         self.assertEqual(dts[0], dts[3])
4296
4297     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4298     @given(data_strategy())
4299     def test_valid_ber(self, d):
4300         min_year = 1901 if PY2 else 2
4301         year = d.draw(integers(min_value=min_year, max_value=9999))
4302         month = d.draw(integers(min_value=1, max_value=12))
4303         day = d.draw(integers(min_value=1, max_value=28))
4304         hours = d.draw(integers(min_value=0, max_value=23))
4305         data = "%04d%02d%02d%02d" % (year, month, day, hours)
4306         dt = datetime(year, month, day, hours)
4307         fractions_sign = d.draw(sampled_from("  ,."))
4308         fractions = None
4309         if fractions_sign != " ":
4310             fractions = random()
4311         if d.draw(booleans()):
4312             minutes = d.draw(integers(min_value=0, max_value=59))
4313             data += "%02d" % minutes
4314             dt += timedelta(seconds=60 * minutes)
4315             if d.draw(booleans()):
4316                 seconds = d.draw(integers(min_value=0, max_value=59))
4317                 data += "%02d" % seconds
4318                 dt += timedelta(seconds=seconds)
4319                 if fractions is not None:
4320                     dt += timedelta(microseconds=10**6 * fractions)
4321             elif fractions is not None:
4322                 dt += timedelta(seconds=60 * fractions)
4323         elif fractions is not None:
4324             dt += timedelta(seconds=3600 * fractions)
4325         if fractions is not None:
4326             data += fractions_sign + str(fractions)[2:]
4327         if d.draw(booleans()):
4328             data += "Z"
4329         elif d.draw(booleans()):
4330             offset_hour = d.draw(integers(min_value=0, max_value=13))
4331             sign = 1
4332             if d.draw(booleans()):
4333                 data += "-"
4334                 sign = -1
4335             else:
4336                 data += "+"
4337             dt -= timedelta(seconds=sign * 3600 * offset_hour)
4338             data += "%02d" % offset_hour
4339             minutes_separator = d.draw(sampled_from((None, "", ":")))
4340             if minutes_separator is not None:
4341                 offset_minute = d.draw(integers(min_value=0, max_value=59))
4342                 dt -= timedelta(seconds=sign * 60 * offset_minute)
4343                 data += "%s%02d" % (minutes_separator, offset_minute)
4344         data = data.encode("ascii")
4345         data_der = GeneralizedTime.tag_default + len_encode(len(data)) + data
4346         try:
4347             GeneralizedTime().decod(data_der)
4348         except DecodeError:
4349             dered = False
4350         else:
4351             dered = True
4352         obj = GeneralizedTime().decod(data_der, ctx={"bered": True})
4353         if dt.year > 1970:
4354             self.assertEqual(
4355                 mktime(obj.todatetime().timetuple()),
4356                 mktime(dt.timetuple()),
4357             )
4358         elif not PY2:
4359             self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
4360         self.assertEqual(obj.ber_encoded, not dered)
4361         self.assertEqual(obj.bered, not dered)
4362         self.assertEqual(obj.ber_raw, None if dered else data)
4363         self.assertEqual(obj.encode() == data_der, dered)
4364         repr(obj)
4365         bytes(obj)
4366         str(obj)
4367
4368     def test_invalid_ber(self):
4369         for data in ((
4370                 # "00010203040506.07",
4371                 "-0010203040506.07",
4372                 "0001-203040506.07",
4373                 "000102-3040506.07",
4374                 "00010203-40506.07",
4375                 "0001020304-506.07",
4376                 "000102030405-6.07",
4377                 "00010203040506.-7",
4378                 "+0010203040506.07",
4379                 "0001+203040506.07",
4380                 "000102+3040506.07",
4381                 "00010203+40506.07",
4382                 "0001020304+506.07",
4383                 "000102030405+6.07",
4384                 "00010203040506.+7",
4385                 " 0010203040506.07",
4386                 "0001 203040506.07",
4387                 "000102 3040506.07",
4388                 "00010203 40506.07",
4389                 "0001020304 506.07",
4390                 "000102030405 6.07",
4391                 "00010203040506. 7",
4392                 "001 0203040506.07",
4393                 "00012 03040506.07",
4394                 "0001023 040506.07",
4395                 "000102034 0506.07",
4396                 "00010203045 06.07",
4397                 "0001020304056 .07",
4398                 "00010203040506.7 ",
4399                 "00010203040506.",
4400                 "0001020304050607",
4401
4402                 "-0010203040506",
4403                 "0001-203040506",
4404                 "000102-3040506",
4405                 "00010203-40506",
4406                 "0001020304-506",
4407                 "000102030405-6",
4408                 "0001+203040506",
4409                 "000102+3040506",
4410                 "00010203+40506",
4411                 "0001020304+506",
4412                 "000102030405+6",
4413                 " 0010203040506",
4414                 "0001 203040506",
4415                 "000102 3040506",
4416                 "00010203 40506",
4417                 "0001020304 506",
4418                 "000102030405 6",
4419                 "001 0203040506",
4420                 "00012 03040506",
4421                 "0001023 040506",
4422                 "000102034 0506",
4423                 "00010203045 06",
4424                 "0001020304056 ",
4425
4426                 "-00102030405.07",
4427                 "0001-2030405.07",
4428                 "000102-30405.07",
4429                 "00010203-405.07",
4430                 "0001020304-5.07",
4431                 "000102030405.-7",
4432                 "+00102030405.07",
4433                 "0001+2030405.07",
4434                 "00010203+405.07",
4435                 "0001020304+5.07",
4436                 "000102030405.+7",
4437                 " 00102030405.07",
4438                 "0001 2030405.07",
4439                 "000102 30405.07",
4440                 "00010203 405.07",
4441                 "0001020304 5.07",
4442                 "000102030405. 7",
4443                 "001 02030405.07",
4444                 "00012 030405.07",
4445                 "0001023 0405.07",
4446                 "000102034 05.07",
4447                 "00010203045 .07",
4448                 "000102030405.7 ",
4449                 "000102030405.",
4450
4451                 "-001020304.07",
4452                 "0001-20304.07",
4453                 "000102-304.07",
4454                 "00010203-4.07",
4455                 "0001020304.-7",
4456                 "+001020304.07",
4457                 "0001+20304.07",
4458                 "00010203+4.07",
4459                 "0001020304.+7",
4460                 " 001020304.07",
4461                 "0001 20304.07",
4462                 "000102 304.07",
4463                 "00010203 4.07",
4464                 "0001020304. 7",
4465                 "001 020304.07",
4466                 "00012 0304.07",
4467                 "0001023 04.07",
4468                 "000102034 .07",
4469                 "0001020304.7 ",
4470                 "0001020304.",
4471
4472                 "00010203",
4473                 "00010203040506Y",
4474                 "0001010100+0001",
4475                 "0001010100+00:01",
4476                 "0001010100+01",
4477
4478                 "00010203040506.07+15",
4479                 "00010203040506.07-15",
4480                 "00010203040506.07+14:60",
4481                 "00010203040506.07+1460",
4482                 "00010203040506.07-1460",
4483                 "00010203040506.07+00:60",
4484                 "00010203040506.07-00:60",
4485
4486                 "00010203040506+15",
4487                 "00010203040506-15",
4488                 "00010203040506+14:60",
4489                 "00010203040506+1460",
4490                 "00010203040506-1460",
4491                 "00010203040506+00:60",
4492                 "00010203040506-00:60",
4493
4494                 "0001020304050.07",
4495                 "00010203040.07",
4496                 "000102030.07",
4497                 "0001020304050",
4498                 "00010203040",
4499                 "000102030",
4500         )):
4501             with self.assertRaises(DecodeError):
4502                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4503             data = data.replace(".", ",")
4504             with self.assertRaises(DecodeError):
4505                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4506
4507     @given(
4508         binary(
4509             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4510             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4511         ),
4512         binary(min_size=1, max_size=1),
4513         binary(
4514             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4515             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4516         ),
4517     )
4518     def test_junk(self, part0, part1, part2):
4519         junk = part0 + part1 + part2
4520         assume(not (set(junk) <= set(digits.encode("ascii"))))
4521         with self.assertRaises(DecodeError):
4522             GeneralizedTime().decode(
4523                 GeneralizedTime.tag_default +
4524                 len_encode(len(junk)) +
4525                 junk
4526             )
4527
4528     @given(
4529         binary(
4530             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4531             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4532         ),
4533         binary(min_size=1, max_size=1),
4534         binary(
4535             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4536             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4537         ),
4538     )
4539     def test_junk_dm(self, part0, part1, part2):
4540         junk = part0 + part1 + part2
4541         assume(not (set(junk) <= set(digits.encode("ascii"))))
4542         with self.assertRaises(DecodeError):
4543             GeneralizedTime().decode(
4544                 GeneralizedTime.tag_default +
4545                 len_encode(len(junk)) +
4546                 junk
4547             )
4548
4549     def test_ns_fractions(self):
4550         GeneralizedTime(b"20010101000000.000001Z")
4551         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
4552             GeneralizedTime(b"20010101000000.0000001Z")
4553
4554     def test_non_pure_integers(self):
4555         for data in ((
4556                 # b"20000102030405Z,
4557                 b"+2000102030405Z",
4558                 b"2000+102030405Z",
4559                 b"200001+2030405Z",
4560                 b"20000102+30405Z",
4561                 b"2000010203+405Z",
4562                 b"200001020304+5Z",
4563                 b"20000102030405.+6Z",
4564                 b"20000102030405.-6Z",
4565                 b"_2000102030405Z",
4566                 b"2000_102030405Z",
4567                 b"200001_2030405Z",
4568                 b"20000102_30405Z",
4569                 b"2000010203_405Z",
4570                 b"200001020304_5Z",
4571                 b"20000102030405._6Z",
4572                 b"20000102030405.6_Z",
4573                 b" 2000102030405Z",
4574                 b"2000 102030405Z",
4575                 b"200001 2030405Z",
4576                 b"20000102 30405Z",
4577                 b"2000010203 405Z",
4578                 b"200001020304 5Z",
4579                 b"20000102030405. 6Z",
4580                 b"200 0102030405Z",
4581                 b"20001 02030405Z",
4582                 b"2000012 030405Z",
4583                 b"200001023 0405Z",
4584                 b"20000102034 05Z",
4585                 b"2000010203045 Z",
4586                 b"20000102030405.6 Z",
4587         )):
4588             with self.assertRaises(DecodeError):
4589                 GeneralizedTime(data)
4590
4591
4592 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
4593     base_klass = UTCTime
4594     omit_ms = True
4595     min_datetime = datetime(2000, 1, 1)
4596     max_datetime = datetime(2049, 12, 31)
4597     evgen_mode_skip_value = False
4598
4599     def additional_symmetric_check(self, value, obj_encoded):
4600         pass
4601
4602     def test_repr_not_ready(self):
4603         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
4604         repr(UTCTime())
4605
4606     def test_x690_vector_valid(self):
4607         for data in ((
4608                 b"920521000000Z",
4609                 b"920622123421Z",
4610                 b"920722132100Z",
4611         )):
4612             UTCTime(data)
4613
4614     def test_x690_vector_invalid(self):
4615         for data in ((
4616                 b"920520240000Z",
4617                 b"9207221321Z",
4618         )):
4619             with self.assertRaises(DecodeError) as err:
4620                 UTCTime(data)
4621             repr(err.exception)
4622
4623     def test_go_vectors_invalid(self):
4624         for data in ((
4625                 b"a10506234540Z",
4626                 b"91a506234540Z",
4627                 b"9105a6234540Z",
4628                 b"910506a34540Z",
4629                 b"910506334a40Z",
4630                 b"91050633444aZ",
4631                 b"910506334461Z",
4632                 b"910506334400Za",
4633                 b"000100000000Z",
4634                 b"101302030405Z",
4635                 b"100002030405Z",
4636                 b"100100030405Z",
4637                 b"100132030405Z",
4638                 b"100231030405Z",
4639                 b"100102240405Z",
4640                 b"100102036005Z",
4641                 b"100102030460Z",
4642                 b"-100102030410Z",
4643                 b"10-0102030410Z",
4644                 b"10-0002030410Z",
4645                 b"1001-02030410Z",
4646                 b"100102-030410Z",
4647                 b"10010203-0410Z",
4648                 b"1001020304-10Z",
4649                 # These ones are INVALID in *DER*, but accepted
4650                 # by Go's encoding/asn1
4651                 b"910506164540-0700",
4652                 b"910506164540+0730",
4653                 b"9105062345Z",
4654                 b"5105062345Z",
4655         )):
4656             with self.assertRaises(DecodeError) as err:
4657                 UTCTime(data)
4658             repr(err.exception)
4659
4660     def test_go_vectors_valid(self):
4661         self.assertEqual(
4662             UTCTime(b"910506234540Z").todatetime(),
4663             datetime(1991, 5, 6, 23, 45, 40, 0),
4664         )
4665
4666     def test_non_pure_integers(self):
4667         for data in ((
4668                 # b"000102030405Z",
4669                 b"+10102030405Z",
4670                 b"00+102030405Z",
4671                 b"0001+2030405Z",
4672                 b"000102+30405Z",
4673                 b"00010203+405Z",
4674                 b"0001020304+5Z",
4675                 b"_10102030405Z",
4676                 b"00_102030405Z",
4677                 b"0001_2030405Z",
4678                 b"000102_30405Z",
4679                 b"00010203_405Z",
4680                 b"0001020304_5Z",
4681                 b"00010203045_Z",
4682                 b" 10102030405Z",
4683                 b"00 102030405Z",
4684                 b"0001 2030405Z",
4685                 b"000102 30405Z",
4686                 b"00010203 405Z",
4687                 b"0001020304 5Z",
4688                 b"1 0102030405Z",
4689                 b"001 02030405Z",
4690                 b"00012 030405Z",
4691                 b"0001023 0405Z",
4692                 b"000102034 05Z",
4693                 b"00010203045 Z",
4694         )):
4695             with self.assertRaises(DecodeError):
4696                 UTCTime(data)
4697
4698     def test_x680_vector_valid_ber(self):
4699         for data, dt in ((
4700                 (b"8201021200Z", datetime(1982, 1, 2, 12)),
4701                 (b"8201020700-0500", datetime(1982, 1, 2, 12)),
4702                 (b"0101021200Z", datetime(2001, 1, 2, 12)),
4703                 (b"0101020700-0500", datetime(2001, 1, 2, 12)),
4704         )):
4705             data_der = UTCTime.tag_default + len_encode(len(data)) + data
4706             obj = UTCTime().decod(data_der, ctx={"bered": True})
4707             self.assertEqual(obj, dt)
4708             self.assertEqual(obj.todatetime(), dt)
4709             self.assertTrue(obj.ber_encoded)
4710             self.assertTrue(obj.bered)
4711             self.assertEqual(obj.ber_raw, data)
4712             self.assertNotEqual(obj.encode(), data_der)
4713             repr(obj)
4714
4715     def test_go_vectors_valid_ber(self):
4716         for data in ((
4717                 b"910506164540-0700",
4718                 b"910506164540+0730",
4719                 b"9105062345Z",
4720                 b"5105062345Z",
4721         )):
4722             data = UTCTime.tag_default + len_encode(len(data)) + data
4723             obj = UTCTime().decod(data, ctx={"bered": True})
4724             self.assertTrue(obj.ber_encoded)
4725             self.assertTrue(obj.bered)
4726             self.assertNotEqual(obj.encode(), data)
4727             repr(obj)
4728
4729     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4730     @given(data_strategy())
4731     def test_valid_ber(self, d):
4732         year = d.draw(integers(min_value=0, max_value=99))
4733         month = d.draw(integers(min_value=1, max_value=12))
4734         day = d.draw(integers(min_value=1, max_value=28))
4735         hours = d.draw(integers(min_value=0, max_value=23))
4736         minute = d.draw(integers(min_value=0, max_value=59))
4737         data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
4738         dt = datetime(
4739             year + (2000 if year < 50 else 1900),
4740             month,
4741             day,
4742             hours,
4743             minute,
4744         )
4745         dered = False
4746         if d.draw(booleans()):
4747             dered = True
4748             seconds = d.draw(integers(min_value=0, max_value=59))
4749             data += "%02d" % seconds
4750             dt += timedelta(seconds=seconds)
4751         if d.draw(booleans()):
4752             data += "Z"
4753         else:
4754             dered = False
4755             offset_hour = d.draw(integers(min_value=0, max_value=13))
4756             offset_minute = d.draw(integers(min_value=0, max_value=59))
4757             offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
4758             if d.draw(booleans()):
4759                 dt += offset
4760                 data += "-"
4761             else:
4762                 dt -= offset
4763                 data += "+"
4764             data += "%02d%02d" % (offset_hour, offset_minute)
4765         data = data.encode("ascii")
4766         data_der = UTCTime.tag_default + len_encode(len(data)) + data
4767         obj = UTCTime().decod(data_der, ctx={"bered": True})
4768         self.assertEqual(obj, dt)
4769         self.assertEqual(obj.todatetime(), dt)
4770         self.assertEqual(obj.ber_encoded, not dered)
4771         self.assertEqual(obj.bered, not dered)
4772         self.assertEqual(obj.ber_raw, None if dered else data)
4773         self.assertEqual(obj.encode() == data_der, dered)
4774         repr(obj)
4775         bytes(obj)
4776         str(obj)
4777
4778     def test_invalid_ber(self):
4779         for data in ((
4780                 # b"0001020304Z",
4781                 b"-101020304Z",
4782                 b"00-1020304Z",
4783                 b"0001-20304Z",
4784                 b"000102-304Z",
4785                 b"000102-104Z",
4786                 b"00000203-4Z",
4787                 b"+101020304Z",
4788                 b"00+1020304Z",
4789                 b"0001+20304Z",
4790                 b"000102+304Z",
4791                 b"000102+104Z",
4792                 b"00000203+4Z",
4793                 b" 101020304Z",
4794                 b"00 1020304Z",
4795                 b"0001 20304Z",
4796                 b"000102 304Z",
4797                 b"000102 104Z",
4798                 b"00000203 4Z",
4799                 b"1 01020304Z",
4800                 b"001 020304Z",
4801                 b"00012 0304Z",
4802                 b"0001023 04Z",
4803                 b"0001021 04Z",
4804                 b"000002034 Z",
4805                 b"0013020304Z",
4806                 b"0001000304Z",
4807                 b"0001320304Z",
4808                 b"0001022404Z",
4809                 b"0001020360Z",
4810                 b"0002300304Z",
4811                 b"0001020304",
4812                 b"0001020304T",
4813                 b"0001020304+",
4814                 b"0001020304-",
4815                 b"0001020304+0",
4816                 b"0001020304+00",
4817                 b"0001020304+000",
4818                 b"0001020304+000Z",
4819                 b"0001020304+0000Z",
4820                 b"0001020304+-101",
4821                 b"0001020304+01-1",
4822                 b"0001020304+0060",
4823                 b"0001020304+1401",
4824                 b"5001010000+0001",
4825                 b"000102030Z",
4826                 b"0001020Z",
4827         )):
4828             with self.assertRaises(DecodeError):
4829                 UTCTime(data, ctx={"bered": True})
4830             data = data[:8] + data[8+2:]
4831             with self.assertRaises(DecodeError):
4832                 UTCTime(data, ctx={"bered": True})
4833
4834         for data in ((
4835                 # b"000102030405Z",
4836                 b"-10102030405Z",
4837                 b"00-102030405Z",
4838                 b"0001-2030405Z",
4839                 b"000102-30405Z",
4840                 b"000102-10405Z",
4841                 b"00000203-405Z",
4842                 b"0000020304-5Z",
4843                 b"+10102030405Z",
4844                 b"00+102030405Z",
4845                 b"0001+2030405Z",
4846                 b"000102+30405Z",
4847                 b"000102+10405Z",
4848                 b"00000203+405Z",
4849                 b"0000020304+5Z",
4850                 b" 10102030405Z",
4851                 b"00 102030405Z",
4852                 b"0001 2030405Z",
4853                 b"000102 30405Z",
4854                 b"000102 10405Z",
4855                 b"00000203 405Z",
4856                 b"0000020304 5Z",
4857                 b"1 0102030405Z",
4858                 b"001 02030405Z",
4859                 b"00012 030405Z",
4860                 b"0001023 0405Z",
4861                 b"0001021 0405Z",
4862                 b"000002034 05Z",
4863                 b"00000203045 Z",
4864                 b"001302030405Z",
4865                 b"000100030405Z",
4866                 b"000132030405Z",
4867                 b"000102240405Z",
4868                 b"000102036005Z",
4869                 b"000230030405Z",
4870                 b"000102030460Z",
4871                 b"000102030405",
4872                 b"000102030405T",
4873                 b"000102030405+",
4874                 b"000102030405-",
4875                 b"000102030405+0",
4876                 b"000102030405+00",
4877                 b"000102030405+000",
4878                 b"000102030405+000Z",
4879                 b"000102030405+0000Z",
4880                 b"000102030405+-101",
4881                 b"000102030405+01-1",
4882                 b"000102030405+0060",
4883                 b"000102030405+1401",
4884                 b"500101000002+0003",
4885         )):
4886             with self.assertRaises(DecodeError):
4887                 UTCTime(data, ctx={"bered": True})
4888
4889     @given(integers(min_value=0, max_value=49))
4890     def test_pre50(self, year):
4891         self.assertEqual(
4892             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4893             2000 + year,
4894         )
4895
4896     @given(integers(min_value=50, max_value=99))
4897     def test_post50(self, year):
4898         self.assertEqual(
4899             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4900             1900 + year,
4901         )
4902
4903     @given(
4904         binary(
4905             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4906             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4907         ),
4908         binary(min_size=1, max_size=1),
4909         binary(
4910             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4911             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4912         ),
4913     )
4914     def test_junk(self, part0, part1, part2):
4915         junk = part0 + part1 + part2
4916         assume(not (set(junk) <= set(digits.encode("ascii"))))
4917         with self.assertRaises(DecodeError):
4918             UTCTime().decode(
4919                 UTCTime.tag_default +
4920                 len_encode(len(junk)) +
4921                 junk
4922             )
4923
4924
4925 @composite
4926 def tlv_value_strategy(draw):
4927     tag_num = draw(integers(min_value=1))
4928     data = draw(binary())
4929     return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
4930
4931
4932 @composite
4933 def any_values_strategy(draw, do_expl=False):
4934     value = draw(one_of(none(), tlv_value_strategy()))
4935     expl = None
4936     if do_expl:
4937         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4938     optional = draw(one_of(none(), booleans()))
4939     _decoded = (
4940         draw(integers(min_value=0)),
4941         draw(integers(min_value=0)),
4942         draw(integers(min_value=0)),
4943     )
4944     return (value, expl, optional, _decoded)
4945
4946
4947 class AnyInherited(Any):
4948     pass
4949
4950
4951 class TestAny(CommonMixin, TestCase):
4952     base_klass = Any
4953
4954     def test_invalid_value_type(self):
4955         with self.assertRaises(InvalidValueType) as err:
4956             Any(123)
4957         repr(err.exception)
4958
4959     @given(booleans())
4960     def test_optional(self, optional):
4961         obj = Any(optional=optional)
4962         self.assertEqual(obj.optional, optional)
4963
4964     @given(tlv_value_strategy())
4965     def test_ready(self, value):
4966         obj = Any()
4967         self.assertFalse(obj.ready)
4968         repr(obj)
4969         list(obj.pps())
4970         pprint(obj, big_blobs=True, with_decode_path=True)
4971         with self.assertRaises(ObjNotReady) as err:
4972             obj.encode()
4973         repr(err.exception)
4974         obj = Any(value)
4975         self.assertTrue(obj.ready)
4976         repr(obj)
4977         list(obj.pps())
4978         pprint(obj, big_blobs=True, with_decode_path=True)
4979
4980     @given(integers())
4981     def test_basic(self, value):
4982         integer_encoded = Integer(value).encode()
4983         for obj in (
4984                 Any(integer_encoded),
4985                 Any(Integer(value)),
4986                 Any(Any(Integer(value))),
4987         ):
4988             self.assertSequenceEqual(bytes(obj), integer_encoded)
4989             self.assertEqual(
4990                 obj.decode(obj.encode())[0].vlen,
4991                 len(integer_encoded),
4992             )
4993             repr(obj)
4994             list(obj.pps())
4995             pprint(obj, big_blobs=True, with_decode_path=True)
4996             self.assertSequenceEqual(obj.encode(), integer_encoded)
4997
4998     @given(tlv_value_strategy(), tlv_value_strategy())
4999     def test_comparison(self, value1, value2):
5000         for klass in (Any, AnyInherited):
5001             obj1 = klass(value1)
5002             obj2 = klass(value2)
5003             self.assertEqual(obj1 == obj2, value1 == value2)
5004             self.assertEqual(obj1 != obj2, value1 != value2)
5005             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
5006
5007     @given(data_strategy())
5008     def test_call(self, d):
5009         for klass in (Any, AnyInherited):
5010             (
5011                 value_initial,
5012                 expl_initial,
5013                 optional_initial,
5014                 _decoded_initial,
5015             ) = d.draw(any_values_strategy())
5016             obj_initial = klass(
5017                 value_initial,
5018                 expl_initial,
5019                 optional_initial or False,
5020                 _decoded_initial,
5021             )
5022             (
5023                 value,
5024                 expl,
5025                 optional,
5026                 _decoded,
5027             ) = d.draw(any_values_strategy(do_expl=True))
5028             obj = obj_initial(value, expl, optional)
5029             if obj.ready:
5030                 value_expected = None if value is None else value
5031                 self.assertEqual(obj, value_expected)
5032             self.assertEqual(obj.expl_tag, expl or expl_initial)
5033             if obj.default is None:
5034                 optional = optional_initial if optional is None else optional
5035                 optional = False if optional is None else optional
5036             self.assertEqual(obj.optional, optional)
5037
5038     def test_simultaneous_impl_expl(self):
5039         # override it, as Any does not have implicit tag
5040         pass
5041
5042     def test_decoded(self):
5043         # override it, as Any does not have implicit tag
5044         pass
5045
5046     @given(any_values_strategy())
5047     def test_copy(self, values):
5048         for klass in (Any, AnyInherited):
5049             obj = klass(*values)
5050             for copy_func in copy_funcs:
5051                 obj_copied = copy_func(obj)
5052                 self.assert_copied_basic_fields(obj, obj_copied)
5053                 self.assertEqual(obj._value, obj_copied._value)
5054
5055     @given(binary().map(OctetString))
5056     def test_stripped(self, value):
5057         obj = Any(value)
5058         with self.assertRaises(NotEnoughData):
5059             obj.decode(obj.encode()[:-1])
5060
5061     @given(
5062         tlv_value_strategy(),
5063         integers(min_value=1).map(tag_ctxc),
5064     )
5065     def test_stripped_expl(self, value, tag_expl):
5066         obj = Any(value, expl=tag_expl)
5067         with self.assertRaises(NotEnoughData):
5068             obj.decode(obj.encode()[:-1])
5069
5070     @given(
5071         integers(min_value=31),
5072         integers(min_value=0),
5073         decode_path_strat,
5074     )
5075     def test_bad_tag(self, tag, offset, decode_path):
5076         with self.assertRaises(DecodeError) as err:
5077             Any().decode(
5078                 tag_encode(tag)[:-1],
5079                 offset=offset,
5080                 decode_path=decode_path,
5081             )
5082         repr(err.exception)
5083         self.assertEqual(err.exception.offset, offset)
5084         self.assertEqual(err.exception.decode_path, decode_path)
5085
5086     @given(
5087         integers(min_value=128),
5088         integers(min_value=0),
5089         decode_path_strat,
5090     )
5091     def test_bad_len(self, l, offset, decode_path):
5092         with self.assertRaises(DecodeError) as err:
5093             Any().decode(
5094                 Any.tag_default + len_encode(l)[:-1],
5095                 offset=offset,
5096                 decode_path=decode_path,
5097             )
5098         repr(err.exception)
5099         self.assertEqual(err.exception.offset, offset)
5100         self.assertEqual(err.exception.decode_path, decode_path)
5101
5102     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5103     @given(
5104         any_values_strategy(),
5105         integers().map(lambda x: Integer(x).encode()),
5106         integers(min_value=1).map(tag_ctxc),
5107         integers(min_value=0),
5108         binary(max_size=5),
5109         decode_path_strat,
5110     )
5111     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
5112         for klass in (Any, AnyInherited):
5113             _, _, optional, _decoded = values
5114             obj = klass(value=value, optional=optional, _decoded=_decoded)
5115             repr(obj)
5116             list(obj.pps())
5117             pprint(obj, big_blobs=True, with_decode_path=True)
5118             self.assertFalse(obj.expled)
5119             tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5120             self.assertEqual(obj.tag_order, (tag_class, tag_num))
5121             obj_encoded = obj.encode()
5122             obj_expled = obj(value, expl=tag_expl)
5123             self.assertTrue(obj_expled.expled)
5124             tag_class, _, tag_num = tag_decode(tag_expl)
5125             self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5126             repr(obj_expled)
5127             list(obj_expled.pps())
5128             pprint(obj_expled, big_blobs=True, with_decode_path=True)
5129             obj_expled_encoded = obj_expled.encode()
5130             ctx_copied = deepcopy(ctx_dummy)
5131             obj_decoded, tail = obj_expled.decode(
5132                 obj_expled_encoded + tail_junk,
5133                 offset=offset,
5134                 ctx=ctx_copied,
5135             )
5136             self.assertDictEqual(ctx_copied, ctx_dummy)
5137             repr(obj_decoded)
5138             list(obj_decoded.pps())
5139             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5140             self.assertEqual(tail, tail_junk)
5141             self.assertEqual(obj_decoded, obj_expled)
5142             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
5143             self.assertEqual(bytes(obj_decoded), bytes(obj))
5144             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5145             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5146             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5147             self.assertEqual(
5148                 obj_decoded.expl_llen,
5149                 len(len_encode(len(obj_encoded))),
5150             )
5151             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5152             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5153             self.assertEqual(
5154                 obj_decoded.offset,
5155                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5156             )
5157             self.assertEqual(obj_decoded.expl_offset, offset)
5158             self.assertEqual(obj_decoded.tlen, 0)
5159             self.assertEqual(obj_decoded.llen, 0)
5160             self.assertEqual(obj_decoded.vlen, len(value))
5161             assert_exceeding_data(
5162                 self,
5163                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5164                 tail_junk,
5165             )
5166
5167             evgens = list(obj_expled.decode_evgen(
5168                 obj_expled_encoded + tail_junk,
5169                 offset=offset,
5170                 decode_path=decode_path,
5171                 ctx=ctx_copied,
5172             ))
5173             self.assertEqual(len(evgens), 1)
5174             _decode_path, obj, tail = evgens[0]
5175             self.assertSequenceEqual(tail, tail_junk)
5176             self.assertEqual(_decode_path, decode_path)
5177             self.assertEqual(obj.expl_offset, offset)
5178             repr(obj)
5179             list(obj.pps())
5180
5181     @given(
5182         integers(min_value=1).map(tag_ctxc),
5183         integers(min_value=0, max_value=3),
5184         integers(min_value=0),
5185         decode_path_strat,
5186         binary(),
5187     )
5188     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
5189         chunk = Boolean(False, expl=expl).encode()
5190         encoded = (
5191             OctetString.tag_default +
5192             LENINDEF +
5193             b"".join([chunk] * chunks) +
5194             EOC
5195         )
5196         with self.assertRaises(LenIndefForm):
5197             Any().decode(
5198                 encoded + junk,
5199                 offset=offset,
5200                 decode_path=decode_path,
5201             )
5202         obj, tail = Any().decode(
5203             encoded + junk,
5204             offset=offset,
5205             decode_path=decode_path,
5206             ctx={"bered": True},
5207         )
5208         self.assertSequenceEqual(tail, junk)
5209         self.assertEqual(obj.offset, offset)
5210         self.assertEqual(obj.tlvlen, len(encoded))
5211         self.assertTrue(obj.lenindef)
5212         self.assertFalse(obj.ber_encoded)
5213         self.assertTrue(obj.bered)
5214         obj = copy(obj)
5215         self.assertTrue(obj.lenindef)
5216         self.assertFalse(obj.ber_encoded)
5217         self.assertTrue(obj.bered)
5218         repr(obj)
5219         list(obj.pps())
5220         pprint(obj, big_blobs=True, with_decode_path=True)
5221         with self.assertRaises(NotEnoughData) as err:
5222             Any().decode(
5223                 encoded[:-1],
5224                 offset=offset,
5225                 decode_path=decode_path,
5226                 ctx={"bered": True},
5227             )
5228         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
5229         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
5230
5231         class SeqOf(SequenceOf):
5232             schema = Boolean(expl=expl)
5233
5234         class Seq(Sequence):
5235             schema = (
5236                 ("type", ObjectIdentifier(defines=((("value",), {
5237                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
5238                 }),))),
5239                 ("value", Any()),
5240             )
5241         seq = Seq((
5242             ("type", ObjectIdentifier("1.2.3")),
5243             ("value", Any(encoded)),
5244         ))
5245         seq_encoded = seq.encode()
5246         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5247         self.assertIsNotNone(seq_decoded["value"].defined)
5248         repr(seq_decoded)
5249         list(seq_decoded.pps())
5250         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5251         self.assertTrue(seq_decoded.bered)
5252         self.assertFalse(seq_decoded["type"].bered)
5253         self.assertTrue(seq_decoded["value"].bered)
5254
5255         chunk = chunk[:-1] + b"\x01"
5256         chunks = b"".join([chunk] * (chunks + 1))
5257         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
5258         seq = Seq((
5259             ("type", ObjectIdentifier("1.2.3")),
5260             ("value", Any(encoded)),
5261         ))
5262         seq_encoded = seq.encode()
5263         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5264         self.assertIsNotNone(seq_decoded["value"].defined)
5265         repr(seq_decoded)
5266         list(seq_decoded.pps())
5267         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5268         self.assertTrue(seq_decoded.bered)
5269         self.assertFalse(seq_decoded["type"].bered)
5270         self.assertTrue(seq_decoded["value"].bered)
5271
5272
5273 @composite
5274 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
5275     if schema is None:
5276         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
5277         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
5278             one_of(
5279                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
5280                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
5281             ),
5282             min_size=len(names),
5283             max_size=len(names),
5284         ))]
5285         schema = [
5286             (name, Integer(**tag_kwargs))
5287             for name, tag_kwargs in zip(names, tags)
5288         ]
5289     value = None
5290     if value_required or draw(booleans()):
5291         value = draw(tuples(
5292             sampled_from([name for name, _ in schema]),
5293             integers().map(Integer),
5294         ))
5295     expl = None
5296     if do_expl:
5297         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5298     default = draw(one_of(
5299         none(),
5300         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
5301     ))
5302     optional = draw(one_of(none(), booleans()))
5303     _decoded = (
5304         draw(integers(min_value=0)),
5305         draw(integers(min_value=0)),
5306         draw(integers(min_value=0)),
5307     )
5308     return (schema, value, expl, default, optional, _decoded)
5309
5310
5311 class ChoiceInherited(Choice):
5312     pass
5313
5314
5315 class TestChoice(CommonMixin, TestCase):
5316     class Wahl(Choice):
5317         schema = (("whatever", Boolean()),)
5318     base_klass = Wahl
5319
5320     def test_schema_required(self):
5321         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5322             Choice()
5323
5324     def test_impl_forbidden(self):
5325         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
5326             Choice(impl=b"whatever")
5327
5328     def test_invalid_value_type(self):
5329         with self.assertRaises(InvalidValueType) as err:
5330             self.base_klass(123)
5331         repr(err.exception)
5332         with self.assertRaises(ObjUnknown) as err:
5333             self.base_klass(("whenever", Boolean(False)))
5334         repr(err.exception)
5335         with self.assertRaises(InvalidValueType) as err:
5336             self.base_klass(("whatever", Integer(123)))
5337         repr(err.exception)
5338
5339     @given(booleans())
5340     def test_optional(self, optional):
5341         obj = self.base_klass(
5342             default=self.base_klass(("whatever", Boolean(False))),
5343             optional=optional,
5344         )
5345         self.assertTrue(obj.optional)
5346
5347     @given(booleans())
5348     def test_ready(self, value):
5349         obj = self.base_klass()
5350         self.assertFalse(obj.ready)
5351         repr(obj)
5352         list(obj.pps())
5353         pprint(obj, big_blobs=True, with_decode_path=True)
5354         self.assertIsNone(obj["whatever"])
5355         with self.assertRaises(ObjNotReady) as err:
5356             obj.encode()
5357         repr(err.exception)
5358         obj["whatever"] = Boolean()
5359         self.assertFalse(obj.ready)
5360         repr(obj)
5361         list(obj.pps())
5362         pprint(obj, big_blobs=True, with_decode_path=True)
5363         obj["whatever"] = Boolean(value)
5364         self.assertTrue(obj.ready)
5365         repr(obj)
5366         list(obj.pps())
5367         pprint(obj, big_blobs=True, with_decode_path=True)
5368
5369     @given(booleans(), booleans())
5370     def test_comparison(self, value1, value2):
5371         class WahlInherited(self.base_klass):
5372             pass
5373         for klass in (self.base_klass, WahlInherited):
5374             obj1 = klass(("whatever", Boolean(value1)))
5375             obj2 = klass(("whatever", Boolean(value2)))
5376             self.assertEqual(obj1 == obj2, value1 == value2)
5377             self.assertEqual(obj1 != obj2, value1 != value2)
5378             self.assertEqual(obj1 == obj2._value, value1 == value2)
5379             self.assertFalse(obj1 == obj2._value[1])
5380
5381     @given(data_strategy())
5382     def test_call(self, d):
5383         for klass in (Choice, ChoiceInherited):
5384             (
5385                 schema_initial,
5386                 value_initial,
5387                 expl_initial,
5388                 default_initial,
5389                 optional_initial,
5390                 _decoded_initial,
5391             ) = d.draw(choice_values_strategy())
5392
5393             class Wahl(klass):
5394                 schema = schema_initial
5395             obj_initial = Wahl(
5396                 value=value_initial,
5397                 expl=expl_initial,
5398                 default=default_initial,
5399                 optional=optional_initial or False,
5400                 _decoded=_decoded_initial,
5401             )
5402             (
5403                 _,
5404                 value,
5405                 expl,
5406                 default,
5407                 optional,
5408                 _decoded,
5409             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
5410             obj = obj_initial(value, expl, default, optional)
5411             if obj.ready:
5412                 value_expected = default if value is None else value
5413                 value_expected = (
5414                     default_initial if value_expected is None
5415                     else value_expected
5416                 )
5417                 self.assertEqual(obj.choice, value_expected[0])
5418                 self.assertEqual(obj.value, int(value_expected[1]))
5419             self.assertEqual(obj.expl_tag, expl or expl_initial)
5420             default_expect = default_initial if default is None else default
5421             if default_expect is not None:
5422                 self.assertEqual(obj.default.choice, default_expect[0])
5423                 self.assertEqual(obj.default.value, int(default_expect[1]))
5424             if obj.default is None:
5425                 optional = optional_initial if optional is None else optional
5426                 optional = False if optional is None else optional
5427             else:
5428                 optional = True
5429             self.assertEqual(obj.optional, optional)
5430             self.assertEqual(obj.specs, obj_initial.specs)
5431
5432     def test_simultaneous_impl_expl(self):
5433         # override it, as Any does not have implicit tag
5434         pass
5435
5436     def test_decoded(self):
5437         # override it, as Any does not have implicit tag
5438         pass
5439
5440     @given(choice_values_strategy())
5441     def test_copy(self, values):
5442         _schema, value, expl, default, optional, _decoded = values
5443
5444         class Wahl(self.base_klass):
5445             schema = _schema
5446         register_class(Wahl)
5447         obj = Wahl(
5448             value=value,
5449             expl=expl,
5450             default=default,
5451             optional=optional or False,
5452             _decoded=_decoded,
5453         )
5454         for copy_func in copy_funcs:
5455             obj_copied = copy_func(obj)
5456             self.assertIsNone(obj.tag)
5457             self.assertIsNone(obj_copied.tag)
5458             # hack for assert_copied_basic_fields
5459             obj.tag = "whatever"
5460             obj_copied.tag = "whatever"
5461             self.assert_copied_basic_fields(obj, obj_copied)
5462             obj.tag = None
5463             self.assertEqual(obj._value, obj_copied._value)
5464             self.assertEqual(obj.specs, obj_copied.specs)
5465
5466     @given(booleans())
5467     def test_stripped(self, value):
5468         obj = self.base_klass(("whatever", Boolean(value)))
5469         with self.assertRaises(NotEnoughData):
5470             obj.decode(obj.encode()[:-1])
5471
5472     @given(
5473         booleans(),
5474         integers(min_value=1).map(tag_ctxc),
5475     )
5476     def test_stripped_expl(self, value, tag_expl):
5477         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
5478         with self.assertRaises(NotEnoughData):
5479             obj.decode(obj.encode()[:-1])
5480
5481     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5482     @given(data_strategy())
5483     def test_symmetric(self, d):
5484         _schema, value, _, default, optional, _decoded = d.draw(
5485             choice_values_strategy(value_required=True)
5486         )
5487         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5488         offset = d.draw(integers(min_value=0))
5489         tail_junk = d.draw(binary(max_size=5))
5490         decode_path = d.draw(decode_path_strat)
5491
5492         class Wahl(self.base_klass):
5493             schema = _schema
5494         obj = Wahl(
5495             value=value,
5496             default=default,
5497             optional=optional,
5498             _decoded=_decoded,
5499         )
5500         repr(obj)
5501         list(obj.pps())
5502         pprint(obj, big_blobs=True, with_decode_path=True)
5503         self.assertFalse(obj.expled)
5504         self.assertEqual(obj.tag_order, obj.value.tag_order)
5505         obj_encoded = obj.encode()
5506         obj_expled = obj(value, expl=tag_expl)
5507         self.assertTrue(obj_expled.expled)
5508         tag_class, _, tag_num = tag_decode(tag_expl)
5509         self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5510         repr(obj_expled)
5511         list(obj_expled.pps())
5512         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5513         obj_expled_encoded = obj_expled.encode()
5514         ctx_copied = deepcopy(ctx_dummy)
5515         obj_decoded, tail = obj_expled.decode(
5516             obj_expled_encoded + tail_junk,
5517             offset=offset,
5518             ctx=ctx_copied,
5519         )
5520         self.assertDictEqual(ctx_copied, ctx_dummy)
5521         repr(obj_decoded)
5522         list(obj_decoded.pps())
5523         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5524         self.assertEqual(tail, tail_junk)
5525         self.assertEqual(obj_decoded, obj_expled)
5526         self.assertEqual(obj_decoded.choice, obj_expled.choice)
5527         self.assertEqual(obj_decoded.value, obj_expled.value)
5528         self.assertEqual(obj_decoded.choice, obj.choice)
5529         self.assertEqual(obj_decoded.value, obj.value)
5530         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5531         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5532         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5533         self.assertEqual(
5534             obj_decoded.expl_llen,
5535             len(len_encode(len(obj_encoded))),
5536         )
5537         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5538         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5539         self.assertEqual(
5540             obj_decoded.offset,
5541             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5542         )
5543         self.assertEqual(obj_decoded.expl_offset, offset)
5544         self.assertSequenceEqual(
5545             obj_expled_encoded[
5546                 obj_decoded.value.fulloffset - offset:
5547                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
5548             ],
5549             obj_encoded,
5550         )
5551         assert_exceeding_data(
5552             self,
5553             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5554             tail_junk,
5555         )
5556
5557         evgens = list(obj_expled.decode_evgen(
5558             obj_expled_encoded + tail_junk,
5559             offset=offset,
5560             decode_path=decode_path,
5561             ctx=ctx_copied,
5562         ))
5563         self.assertEqual(len(evgens), 2)
5564         _decode_path, obj, tail = evgens[0]
5565         self.assertEqual(_decode_path, decode_path + (obj_decoded.choice,))
5566         _decode_path, obj, tail = evgens[1]
5567         self.assertSequenceEqual(tail, tail_junk)
5568         self.assertEqual(_decode_path, decode_path)
5569         self.assertEqual(obj.expl_offset, offset)
5570         repr(obj)
5571         list(obj.pps())
5572
5573     @given(integers())
5574     def test_set_get(self, value):
5575         class Wahl(Choice):
5576             schema = (
5577                 ("erste", Boolean()),
5578                 ("zweite", Integer()),
5579             )
5580         obj = Wahl()
5581         with self.assertRaises(ObjUnknown) as err:
5582             obj["whatever"] = "whenever"
5583         with self.assertRaises(InvalidValueType) as err:
5584             obj["zweite"] = Boolean(False)
5585         obj["zweite"] = Integer(value)
5586         repr(err.exception)
5587         with self.assertRaises(ObjUnknown) as err:
5588             obj["whatever"]
5589         repr(err.exception)
5590         self.assertIsNone(obj["erste"])
5591         self.assertEqual(obj["zweite"], Integer(value))
5592
5593     def test_tag_mismatch(self):
5594         class Wahl(Choice):
5595             schema = (
5596                 ("erste", Boolean()),
5597             )
5598         int_encoded = Integer(123).encode()
5599         bool_encoded = Boolean(False).encode()
5600         obj = Wahl()
5601         obj.decode(bool_encoded)
5602         with self.assertRaises(TagMismatch):
5603             obj.decode(int_encoded)
5604
5605     def test_tag_mismatch_underlying(self):
5606         class SeqOfBoolean(SequenceOf):
5607             schema = Boolean()
5608
5609         class SeqOfInteger(SequenceOf):
5610             schema = Integer()
5611
5612         class Wahl(Choice):
5613             schema = (
5614                 ("erste", SeqOfBoolean()),
5615             )
5616
5617         int_encoded = SeqOfInteger((Integer(123),)).encode()
5618         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
5619         obj = Wahl()
5620         obj.decode(bool_encoded)
5621         with self.assertRaises(TagMismatch) as err:
5622             obj.decode(int_encoded)
5623         self.assertEqual(err.exception.decode_path, ("erste", "0"))
5624
5625
5626 @composite
5627 def seq_values_strategy(draw, seq_klass, do_expl=False):
5628     value = None
5629     if draw(booleans()):
5630         value = seq_klass()
5631         value._value = draw(dictionaries(
5632             integers(),
5633             one_of(
5634                 booleans().map(Boolean),
5635                 integers().map(Integer),
5636             ),
5637         ))
5638     schema = None
5639     if draw(booleans()):
5640         schema = list(draw(dictionaries(
5641             integers(),
5642             one_of(
5643                 booleans().map(Boolean),
5644                 integers().map(Integer),
5645             ),
5646         )).items())
5647     impl = None
5648     expl = None
5649     if do_expl:
5650         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5651     else:
5652         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5653     default = None
5654     if draw(booleans()):
5655         default = seq_klass()
5656         default._value = draw(dictionaries(
5657             integers(),
5658             one_of(
5659                 booleans().map(Boolean),
5660                 integers().map(Integer),
5661             ),
5662         ))
5663     optional = draw(one_of(none(), booleans()))
5664     _decoded = (
5665         draw(integers(min_value=0)),
5666         draw(integers(min_value=0)),
5667         draw(integers(min_value=0)),
5668     )
5669     return (value, schema, impl, expl, default, optional, _decoded)
5670
5671
5672 @composite
5673 def sequence_strategy(draw, seq_klass):
5674     inputs = draw(lists(
5675         one_of(
5676             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
5677             tuples(just(Integer), integers(), one_of(none(), integers())),
5678         ),
5679         max_size=6,
5680     ))
5681     tags = draw(sets(
5682         integers(min_value=1),
5683         min_size=len(inputs),
5684         max_size=len(inputs),
5685     ))
5686     inits = [
5687         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5688         for tag, expled in zip(tags, draw(lists(
5689             booleans(),
5690             min_size=len(inputs),
5691             max_size=len(inputs),
5692         )))
5693     ]
5694     empties = []
5695     for i, optional in enumerate(draw(lists(
5696             sampled_from(("required", "optional", "empty")),
5697             min_size=len(inputs),
5698             max_size=len(inputs),
5699     ))):
5700         if optional in ("optional", "empty"):
5701             inits[i]["optional"] = True
5702         if optional == "empty":
5703             empties.append(i)
5704     empties = set(empties)
5705     names = list(draw(sets(
5706         text_printable,
5707         min_size=len(inputs),
5708         max_size=len(inputs),
5709     )))
5710     schema = []
5711     for i, (klass, value, default) in enumerate(inputs):
5712         schema.append((names[i], klass(default=default, **inits[i])))
5713     seq_name = draw(text_letters())
5714     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5715     seq = Seq()
5716     expects = []
5717     for i, (klass, value, default) in enumerate(inputs):
5718         name = names[i]
5719         _, spec = schema[i]
5720         expect = {
5721             "name": name,
5722             "optional": False,
5723             "presented": False,
5724             "default_value": None if spec.default is None else default,
5725             "value": None,
5726         }
5727         if i in empties:
5728             expect["optional"] = True
5729         else:
5730             expect["presented"] = True
5731             expect["value"] = value
5732             if spec.optional:
5733                 expect["optional"] = True
5734             if default is not None and default == value:
5735                 expect["presented"] = False
5736             seq[name] = klass(value)
5737         expects.append(expect)
5738     return seq, expects
5739
5740
5741 @composite
5742 def sequences_strategy(draw, seq_klass):
5743     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
5744     inits = [
5745         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5746         for tag, expled in zip(tags, draw(lists(
5747             booleans(),
5748             min_size=len(tags),
5749             max_size=len(tags),
5750         )))
5751     ]
5752     defaulted = set(
5753         i for i, is_default in enumerate(draw(lists(
5754             booleans(),
5755             min_size=len(tags),
5756             max_size=len(tags),
5757         ))) if is_default
5758     )
5759     names = list(draw(sets(
5760         text_printable,
5761         min_size=len(tags),
5762         max_size=len(tags),
5763     )))
5764     seq_expectses = draw(lists(
5765         sequence_strategy(seq_klass=seq_klass),
5766         min_size=len(tags),
5767         max_size=len(tags),
5768     ))
5769     seqs = [seq for seq, _ in seq_expectses]
5770     schema = []
5771     for i, (name, seq) in enumerate(zip(names, seqs)):
5772         schema.append((
5773             name,
5774             seq(default=(seq if i in defaulted else None), **inits[i]),
5775         ))
5776     seq_name = draw(text_letters())
5777     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5778     seq_outer = Seq()
5779     expect_outers = []
5780     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
5781         expect = {
5782             "name": name,
5783             "expects": expects_inner,
5784             "presented": False,
5785         }
5786         seq_outer[name] = seq_inner
5787         if seq_outer.specs[name].default is None:
5788             expect["presented"] = True
5789         expect_outers.append(expect)
5790     return seq_outer, expect_outers
5791
5792
5793 class SeqMixing(object):
5794     def test_invalid_value_type(self):
5795         with self.assertRaises(InvalidValueType) as err:
5796             self.base_klass(123)
5797         repr(err.exception)
5798
5799     def test_invalid_value_type_set(self):
5800         class Seq(self.base_klass):
5801             schema = (("whatever", Boolean()),)
5802         seq = Seq()
5803         with self.assertRaises(InvalidValueType) as err:
5804             seq["whatever"] = Integer(123)
5805         repr(err.exception)
5806
5807     @given(booleans())
5808     def test_optional(self, optional):
5809         obj = self.base_klass(default=self.base_klass(), optional=optional)
5810         self.assertTrue(obj.optional)
5811
5812     @given(data_strategy())
5813     def test_ready(self, d):
5814         ready = {
5815             str(i): v for i, v in enumerate(d.draw(lists(
5816                 booleans(),
5817                 min_size=1,
5818                 max_size=3,
5819             )))
5820         }
5821         non_ready = {
5822             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
5823                 booleans(),
5824                 min_size=1,
5825                 max_size=3,
5826             )))
5827         }
5828         schema_input = []
5829         for name in d.draw(permutations(
5830                 list(ready.keys()) + list(non_ready.keys()),
5831         )):
5832             schema_input.append((name, Boolean()))
5833
5834         class Seq(self.base_klass):
5835             schema = tuple(schema_input)
5836         seq = Seq()
5837         for name in ready.keys():
5838             seq[name]
5839             seq[name] = Boolean()
5840         self.assertFalse(seq.ready)
5841         repr(seq)
5842         list(seq.pps())
5843         pprint(seq, big_blobs=True, with_decode_path=True)
5844         for name, value in ready.items():
5845             seq[name] = Boolean(value)
5846         self.assertFalse(seq.ready)
5847         repr(seq)
5848         list(seq.pps())
5849         pprint(seq, big_blobs=True, with_decode_path=True)
5850         with self.assertRaises(ObjNotReady) as err:
5851             seq.encode()
5852         repr(err.exception)
5853         for name, value in non_ready.items():
5854             seq[name] = Boolean(value)
5855         self.assertTrue(seq.ready)
5856         repr(seq)
5857         list(seq.pps())
5858         pprint(seq, big_blobs=True, with_decode_path=True)
5859
5860     @given(data_strategy())
5861     def test_call(self, d):
5862         class SeqInherited(self.base_klass):
5863             pass
5864         for klass in (self.base_klass, SeqInherited):
5865             (
5866                 value_initial,
5867                 schema_initial,
5868                 impl_initial,
5869                 expl_initial,
5870                 default_initial,
5871                 optional_initial,
5872                 _decoded_initial,
5873             ) = d.draw(seq_values_strategy(seq_klass=klass))
5874             obj_initial = klass(
5875                 value_initial,
5876                 schema_initial,
5877                 impl_initial,
5878                 expl_initial,
5879                 default_initial,
5880                 optional_initial or False,
5881                 _decoded_initial,
5882             )
5883             (
5884                 value,
5885                 _,
5886                 impl,
5887                 expl,
5888                 default,
5889                 optional,
5890                 _decoded,
5891             ) = d.draw(seq_values_strategy(
5892                 seq_klass=klass,
5893                 do_expl=impl_initial is None,
5894             ))
5895             obj = obj_initial(value, impl, expl, default, optional)
5896             value_expected = default if value is None else value
5897             value_expected = (
5898                 default_initial if value_expected is None
5899                 else value_expected
5900             )
5901             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
5902             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5903             self.assertEqual(obj.expl_tag, expl or expl_initial)
5904             self.assertEqual(
5905                 {} if obj.default is None else obj.default._value,
5906                 getattr(default_initial if default is None else default, "_value", {}),
5907             )
5908             if obj.default is None:
5909                 optional = optional_initial if optional is None else optional
5910                 optional = False if optional is None else optional
5911             else:
5912                 optional = True
5913             self.assertEqual(list(obj.specs.items()), schema_initial or [])
5914             self.assertEqual(obj.optional, optional)
5915
5916     @given(data_strategy())
5917     def test_copy(self, d):
5918         class SeqInherited(self.base_klass):
5919             pass
5920         register_class(SeqInherited)
5921         for klass in (self.base_klass, SeqInherited):
5922             values = d.draw(seq_values_strategy(seq_klass=klass))
5923             obj = klass(*values)
5924             for copy_func in copy_funcs:
5925                 obj_copied = copy_func(obj)
5926                 self.assert_copied_basic_fields(obj, obj_copied)
5927                 self.assertEqual(obj.specs, obj_copied.specs)
5928                 self.assertEqual(obj._value, obj_copied._value)
5929
5930     @given(data_strategy())
5931     def test_stripped(self, d):
5932         value = d.draw(integers())
5933         tag_impl = tag_encode(d.draw(integers(min_value=1)))
5934
5935         class Seq(self.base_klass):
5936             impl = tag_impl
5937             schema = (("whatever", Integer()),)
5938         seq = Seq()
5939         seq["whatever"] = Integer(value)
5940         with self.assertRaises(NotEnoughData):
5941             seq.decode(seq.encode()[:-1])
5942
5943     @given(data_strategy())
5944     def test_stripped_expl(self, d):
5945         value = d.draw(integers())
5946         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5947
5948         class Seq(self.base_klass):
5949             expl = tag_expl
5950             schema = (("whatever", Integer()),)
5951         seq = Seq()
5952         seq["whatever"] = Integer(value)
5953         with self.assertRaises(NotEnoughData):
5954             seq.decode(seq.encode()[:-1])
5955
5956     @given(integers(min_value=3), binary(min_size=2))
5957     def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
5958         junk = tag_encode(junk_tag_num) + junk
5959         try:
5960             _, _, len_encoded = tag_strip(memoryview(junk))
5961             len_decode(len_encoded)
5962         except Exception:
5963             assume(True)
5964         else:
5965             assume(False)
5966
5967         class Seq(self.base_klass):
5968             schema = (
5969                 ("whatever", Integer()),
5970                 ("junk", Any()),
5971                 ("whenever", Integer()),
5972             )
5973         seq = Seq()
5974         seq["whatever"] = Integer(123)
5975         seq["junk"] = Any(junk)
5976         seq["whenever"] = Integer(123)
5977         with self.assertRaises(DecodeError):
5978             seq.decode(seq.encode())
5979
5980     @given(
5981         integers(min_value=31),
5982         integers(min_value=0),
5983         decode_path_strat,
5984     )
5985     def test_bad_tag(self, tag, offset, decode_path):
5986         with self.assertRaises(DecodeError) as err:
5987             self.base_klass().decode(
5988                 tag_encode(tag)[:-1],
5989                 offset=offset,
5990                 decode_path=decode_path,
5991             )
5992         repr(err.exception)
5993         self.assertEqual(err.exception.offset, offset)
5994         self.assertEqual(err.exception.decode_path, decode_path)
5995
5996     @given(
5997         integers(min_value=128),
5998         integers(min_value=0),
5999         decode_path_strat,
6000     )
6001     def test_bad_len(self, l, offset, decode_path):
6002         with self.assertRaises(DecodeError) as err:
6003             self.base_klass().decode(
6004                 self.base_klass.tag_default + len_encode(l)[:-1],
6005                 offset=offset,
6006                 decode_path=decode_path,
6007             )
6008         repr(err.exception)
6009         self.assertEqual(err.exception.offset, offset)
6010         self.assertEqual(err.exception.decode_path, decode_path)
6011
6012     def _assert_expects(self, seq, expects):
6013         for expect in expects:
6014             self.assertEqual(
6015                 seq.specs[expect["name"]].optional,
6016                 expect["optional"],
6017             )
6018             if expect["default_value"] is not None:
6019                 self.assertEqual(
6020                     seq.specs[expect["name"]].default,
6021                     expect["default_value"],
6022                 )
6023             if expect["presented"]:
6024                 self.assertIn(expect["name"], seq)
6025                 self.assertEqual(seq[expect["name"]], expect["value"])
6026
6027     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6028     @given(data_strategy())
6029     def test_symmetric(self, d):
6030         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
6031         tail_junk = d.draw(binary(max_size=5))
6032         decode_path = d.draw(decode_path_strat)
6033         self.assertTrue(seq.ready)
6034         self.assertFalse(seq.decoded)
6035         self._assert_expects(seq, expects)
6036         repr(seq)
6037         list(seq.pps())
6038         pprint(seq, big_blobs=True, with_decode_path=True)
6039         self.assertTrue(seq.ready)
6040         seq_encoded = seq.encode()
6041         seq_encoded_cer = encode_cer(seq)
6042         self.assertNotEqual(seq_encoded_cer, seq_encoded)
6043         self.assertSequenceEqual(
6044             seq.decod(seq_encoded_cer, ctx={"bered": True}).encode(),
6045             seq_encoded,
6046         )
6047         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
6048         self.assertFalse(seq_decoded.lenindef)
6049         self.assertFalse(seq_decoded.ber_encoded)
6050         self.assertFalse(seq_decoded.bered)
6051
6052         t, _, lv = tag_strip(seq_encoded)
6053         _, _, v = len_decode(lv)
6054         seq_encoded_lenindef = t + LENINDEF + v + EOC
6055         with self.assertRaises(DecodeError):
6056             seq.decode(seq_encoded_lenindef)
6057         ctx_copied = deepcopy(ctx_dummy)
6058         ctx_copied["bered"] = True
6059         seq_decoded_lenindef, tail_lenindef = seq.decode(
6060             seq_encoded_lenindef + tail_junk,
6061             ctx=ctx_copied,
6062         )
6063         del ctx_copied["bered"]
6064         self.assertDictEqual(ctx_copied, ctx_dummy)
6065         self.assertTrue(seq_decoded_lenindef.lenindef)
6066         self.assertTrue(seq_decoded_lenindef.bered)
6067         seq_decoded_lenindef = copy(seq_decoded_lenindef)
6068         self.assertTrue(seq_decoded_lenindef.lenindef)
6069         self.assertTrue(seq_decoded_lenindef.bered)
6070         with self.assertRaises(DecodeError):
6071             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
6072         with self.assertRaises(DecodeError):
6073             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
6074         repr(seq_decoded_lenindef)
6075         list(seq_decoded_lenindef.pps())
6076         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
6077         self.assertTrue(seq_decoded_lenindef.ready)
6078
6079         for decoded, decoded_tail, encoded in (
6080                 (seq_decoded, tail, seq_encoded),
6081                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
6082         ):
6083             self.assertEqual(decoded_tail, tail_junk)
6084             self._assert_expects(decoded, expects)
6085             self.assertEqual(seq, decoded)
6086             self.assertEqual(decoded.encode(), seq_encoded)
6087             self.assertEqual(decoded.tlvlen, len(encoded))
6088             for expect in expects:
6089                 if not expect["presented"]:
6090                     self.assertNotIn(expect["name"], decoded)
6091                     continue
6092                 self.assertIn(expect["name"], decoded)
6093                 obj = decoded[expect["name"]]
6094                 self.assertTrue(obj.decoded)
6095                 offset = obj.expl_offset if obj.expled else obj.offset
6096                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6097                 self.assertSequenceEqual(
6098                     seq_encoded[offset:offset + tlvlen],
6099                     obj.encode(),
6100                 )
6101
6102             evgens = list(seq.decode_evgen(
6103                 encoded + decoded_tail,
6104                 decode_path=decode_path,
6105                 ctx={"bered": True},
6106             ))
6107             self.assertEqual(len(evgens), len(list(decoded._values_for_encoding())) + 1)
6108             for _decode_path, obj, _ in evgens[:-1]:
6109                 self.assertEqual(_decode_path[:-1], decode_path)
6110                 repr(obj)
6111                 list(obj.pps())
6112             _decode_path, obj, tail = evgens[-1]
6113             self.assertEqual(_decode_path, decode_path)
6114             repr(obj)
6115             list(obj.pps())
6116
6117         assert_exceeding_data(
6118             self,
6119             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
6120             tail_junk,
6121         )
6122
6123     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6124     @given(data_strategy())
6125     def test_symmetric_with_seq(self, d):
6126         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
6127         self.assertTrue(seq.ready)
6128         seq_encoded = seq.encode()
6129         seq_decoded, tail = seq.decode(seq_encoded)
6130         self.assertEqual(tail, b"")
6131         self.assertTrue(seq.ready)
6132         self.assertEqual(seq, seq_decoded)
6133         self.assertEqual(seq_decoded.encode(), seq_encoded)
6134         for expect_outer in expect_outers:
6135             if not expect_outer["presented"]:
6136                 self.assertNotIn(expect_outer["name"], seq_decoded)
6137                 continue
6138             self.assertIn(expect_outer["name"], seq_decoded)
6139             obj = seq_decoded[expect_outer["name"]]
6140             self.assertTrue(obj.decoded)
6141             offset = obj.expl_offset if obj.expled else obj.offset
6142             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6143             self.assertSequenceEqual(
6144                 seq_encoded[offset:offset + tlvlen],
6145                 obj.encode(),
6146             )
6147             self._assert_expects(obj, expect_outer["expects"])
6148
6149     @given(data_strategy())
6150     def test_default_disappears(self, d):
6151         _schema = list(d.draw(dictionaries(
6152             text_letters(),
6153             sets(integers(), min_size=2, max_size=2),
6154             min_size=1,
6155         )).items())
6156
6157         class Seq(self.base_klass):
6158             schema = [
6159                 (n, Integer(default=d))
6160                 for n, (_, d) in _schema
6161             ]
6162         seq = Seq()
6163         for name, (value, _) in _schema:
6164             seq[name] = Integer(value)
6165         self.assertEqual(len(seq._value), len(_schema))
6166         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
6167         self.assertGreater(len(seq.encode()), len(empty_seq))
6168         for name, (_, default) in _schema:
6169             seq[name] = Integer(default)
6170         self.assertEqual(len(seq._value), 0)
6171         self.assertSequenceEqual(seq.encode(), empty_seq)
6172
6173     @given(data_strategy())
6174     def test_encoded_default_not_accepted(self, d):
6175         _schema = list(d.draw(dictionaries(
6176             text_letters(),
6177             integers(),
6178             min_size=1,
6179         )).items())
6180         tags = [tag_encode(tag) for tag in d.draw(sets(
6181             integers(min_value=1),
6182             min_size=len(_schema),
6183             max_size=len(_schema),
6184         ))]
6185
6186         class SeqWithoutDefault(self.base_klass):
6187             schema = [
6188                 (n, Integer(impl=t))
6189                 for (n, _), t in zip(_schema, tags)
6190             ]
6191         seq_without_default = SeqWithoutDefault()
6192         for name, value in _schema:
6193             seq_without_default[name] = Integer(value)
6194         seq_encoded = seq_without_default.encode()
6195
6196         class SeqWithDefault(self.base_klass):
6197             schema = [
6198                 (n, Integer(default=v, impl=t))
6199                 for (n, v), t in zip(_schema, tags)
6200             ]
6201         seq_with_default = SeqWithDefault()
6202         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6203             seq_with_default.decode(seq_encoded)
6204         for ctx in ({"bered": True}, {"allow_default_values": True}):
6205             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
6206             self.assertTrue(seq_decoded.ber_encoded)
6207             self.assertTrue(seq_decoded.bered)
6208             seq_decoded = copy(seq_decoded)
6209             self.assertTrue(seq_decoded.ber_encoded)
6210             self.assertTrue(seq_decoded.bered)
6211             for name, value in _schema:
6212                 self.assertEqual(seq_decoded[name], seq_with_default[name])
6213                 self.assertEqual(seq_decoded[name], value)
6214
6215     @given(data_strategy())
6216     def test_missing_from_spec(self, d):
6217         names = list(d.draw(sets(text_letters(), min_size=2)))
6218         tags = [tag_encode(tag) for tag in d.draw(sets(
6219             integers(min_value=1),
6220             min_size=len(names),
6221             max_size=len(names),
6222         ))]
6223         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
6224
6225         class SeqFull(self.base_klass):
6226             schema = [(n, Integer(impl=t)) for n, t in names_tags]
6227         seq_full = SeqFull()
6228         for i, name in enumerate(names):
6229             seq_full[name] = Integer(i)
6230         seq_encoded = seq_full.encode()
6231         altered = names_tags[:-2] + names_tags[-1:]
6232
6233         class SeqMissing(self.base_klass):
6234             schema = [(n, Integer(impl=t)) for n, t in altered]
6235         seq_missing = SeqMissing()
6236         with self.assertRaises(TagMismatch):
6237             seq_missing.decode(seq_encoded)
6238
6239     def test_bered(self):
6240         class Seq(self.base_klass):
6241             schema = (("underlying", Boolean()),)
6242         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
6243         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6244         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6245         self.assertFalse(decoded.ber_encoded)
6246         self.assertFalse(decoded.lenindef)
6247         self.assertTrue(decoded.bered)
6248         decoded = copy(decoded)
6249         self.assertFalse(decoded.ber_encoded)
6250         self.assertFalse(decoded.lenindef)
6251         self.assertTrue(decoded.bered)
6252
6253         class Seq(self.base_klass):
6254             schema = (("underlying", OctetString()),)
6255         encoded = (
6256             tag_encode(form=TagFormConstructed, num=4) +
6257             LENINDEF +
6258             OctetString(b"whatever").encode() +
6259             EOC
6260         )
6261         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6262         with self.assertRaises(DecodeError):
6263             Seq().decode(encoded)
6264         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6265         self.assertFalse(decoded.ber_encoded)
6266         self.assertFalse(decoded.lenindef)
6267         self.assertTrue(decoded.bered)
6268         decoded = copy(decoded)
6269         self.assertFalse(decoded.ber_encoded)
6270         self.assertFalse(decoded.lenindef)
6271         self.assertTrue(decoded.bered)
6272
6273
6274 class TestSequence(SeqMixing, CommonMixin, TestCase):
6275     base_klass = Sequence
6276
6277     @given(
6278         integers(),
6279         binary(min_size=1),
6280     )
6281     def test_remaining(self, value, junk):
6282         class Seq(Sequence):
6283             schema = (
6284                 ("whatever", Integer()),
6285             )
6286         int_encoded = Integer(value).encode()
6287         junked = b"".join((
6288             Sequence.tag_default,
6289             len_encode(len(int_encoded + junk)),
6290             int_encoded + junk,
6291         ))
6292         with assertRaisesRegex(self, DecodeError, "remaining"):
6293             Seq().decode(junked)
6294
6295     @given(sets(text_letters(), min_size=2))
6296     def test_obj_unknown(self, names):
6297         missing = names.pop()
6298
6299         class Seq(Sequence):
6300             schema = [(n, Boolean()) for n in names]
6301         seq = Seq()
6302         with self.assertRaises(ObjUnknown) as err:
6303             seq[missing]
6304         repr(err.exception)
6305         with self.assertRaises(ObjUnknown) as err:
6306             seq[missing] = Boolean()
6307         repr(err.exception)
6308
6309     def test_x690_vector(self):
6310         class Seq(Sequence):
6311             schema = (
6312                 ("name", IA5String()),
6313                 ("ok", Boolean()),
6314             )
6315         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
6316         self.assertEqual(seq["name"], "Smith")
6317         self.assertEqual(seq["ok"], True)
6318
6319
6320 class TestSet(SeqMixing, CommonMixin, TestCase):
6321     base_klass = Set
6322
6323     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6324     @given(data_strategy())
6325     def test_sorted(self, d):
6326         class DummySeq(Sequence):
6327             schema = (("null", Null()),)
6328
6329         tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
6330         _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
6331         assume(any(i > dummy_seq_tag_num for i in tag_nums))
6332         tag_nums -= set([dummy_seq_tag_num])
6333         _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
6334         _schema.append(("seq", DummySeq()))
6335
6336         class Seq(Set):
6337             schema = d.draw(permutations(_schema))
6338         seq = Seq()
6339         for name, _ in _schema:
6340             if name != "seq":
6341                 seq[name] = OctetString(name.encode("ascii"))
6342         seq["seq"] = DummySeq((("null", Null()),))
6343
6344         seq_encoded = seq.encode()
6345         seq_decoded, _ = seq.decode(seq_encoded)
6346         seq_encoded_expected = []
6347         for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
6348             if tag_num == dummy_seq_tag_num:
6349                 seq_encoded_expected.append(seq["seq"].encode())
6350             else:
6351                 seq_encoded_expected.append(seq[str(tag_num)].encode())
6352         self.assertSequenceEqual(
6353             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6354             b"".join(seq_encoded_expected),
6355         )
6356
6357         encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
6358         encoded += seq["seq"].encode()
6359         seq_encoded = b"".join((
6360             Set.tag_default,
6361             len_encode(len(encoded)),
6362             encoded,
6363         ))
6364         with assertRaisesRegex(self, DecodeError, "unordered SET"):
6365             seq.decode(seq_encoded)
6366         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6367             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6368             self.assertTrue(seq_decoded.ber_encoded)
6369             self.assertTrue(seq_decoded.bered)
6370             seq_decoded = copy(seq_decoded)
6371             self.assertTrue(seq_decoded.ber_encoded)
6372             self.assertTrue(seq_decoded.bered)
6373
6374     def test_same_value_twice(self):
6375         class Seq(Set):
6376             schema = (
6377                 ("bool", Boolean()),
6378                 ("int", Integer()),
6379             )
6380
6381         encoded = b"".join((
6382             Integer(123).encode(),
6383             Integer(234).encode(),
6384             Boolean(True).encode(),
6385         ))
6386         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6387         with self.assertRaises(TagMismatch):
6388             Seq().decod(encoded, ctx={"allow_unordered_set": True})
6389
6390
6391 @composite
6392 def seqof_values_strategy(draw, schema=None, do_expl=False):
6393     if schema is None:
6394         schema = draw(sampled_from((Boolean(), Integer())))
6395     bound_min, bound_max = sorted(draw(sets(
6396         integers(min_value=0, max_value=10),
6397         min_size=2,
6398         max_size=2,
6399     )))
6400     if isinstance(schema, Boolean):
6401         values_generator = booleans().map(Boolean)
6402     elif isinstance(schema, Integer):
6403         values_generator = integers().map(Integer)
6404     values_generator = lists(
6405         values_generator,
6406         min_size=bound_min,
6407         max_size=bound_max,
6408     )
6409     values = draw(one_of(none(), values_generator))
6410     impl = None
6411     expl = None
6412     if do_expl:
6413         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6414     else:
6415         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6416     default = draw(one_of(none(), values_generator))
6417     optional = draw(one_of(none(), booleans()))
6418     _decoded = (
6419         draw(integers(min_value=0)),
6420         draw(integers(min_value=0)),
6421         draw(integers(min_value=0)),
6422     )
6423     return (
6424         schema,
6425         values,
6426         (bound_min, bound_max),
6427         impl,
6428         expl,
6429         default,
6430         optional,
6431         _decoded,
6432     )
6433
6434
6435 class SeqOfMixing(object):
6436     def test_invalid_value_type(self):
6437         with self.assertRaises(InvalidValueType) as err:
6438             self.base_klass(123)
6439         repr(err.exception)
6440
6441     def test_invalid_values_type(self):
6442         class SeqOf(self.base_klass):
6443             schema = Integer()
6444         with self.assertRaises(InvalidValueType) as err:
6445             SeqOf([Integer(123), Boolean(False), Integer(234)])
6446         repr(err.exception)
6447
6448     def test_schema_required(self):
6449         with assertRaisesRegex(self, ValueError, "schema must be specified"):
6450             self.base_klass.__mro__[1]()
6451
6452     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
6453     def test_comparison(self, value1, value2, tag1, tag2):
6454         class SeqOf(self.base_klass):
6455             schema = Boolean()
6456         obj1 = SeqOf([Boolean(value1)])
6457         obj2 = SeqOf([Boolean(value2)])
6458         self.assertEqual(obj1 == obj2, value1 == value2)
6459         self.assertEqual(obj1 != obj2, value1 != value2)
6460         self.assertEqual(obj1 == list(obj2), value1 == value2)
6461         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
6462         obj1 = SeqOf([Boolean(value1)], impl=tag1)
6463         obj2 = SeqOf([Boolean(value1)], impl=tag2)
6464         self.assertEqual(obj1 == obj2, tag1 == tag2)
6465         self.assertEqual(obj1 != obj2, tag1 != tag2)
6466
6467     @given(lists(booleans()))
6468     def test_iter(self, values):
6469         class SeqOf(self.base_klass):
6470             schema = Boolean()
6471         obj = SeqOf([Boolean(value) for value in values])
6472         self.assertEqual(len(obj), len(values))
6473         for i, value in enumerate(obj):
6474             self.assertEqual(value, values[i])
6475
6476     @given(data_strategy())
6477     def test_ready(self, d):
6478         ready = [Integer(v) for v in d.draw(lists(
6479             integers(),
6480             min_size=1,
6481             max_size=3,
6482         ))]
6483         non_ready = [
6484             Integer() for _ in
6485             range(d.draw(integers(min_value=1, max_value=5)))
6486         ]
6487
6488         class SeqOf(self.base_klass):
6489             schema = Integer()
6490         values = d.draw(permutations(ready + non_ready))
6491         seqof = SeqOf()
6492         for value in values:
6493             seqof.append(value)
6494         self.assertFalse(seqof.ready)
6495         repr(seqof)
6496         list(seqof.pps())
6497         pprint(seqof, big_blobs=True, with_decode_path=True)
6498         with self.assertRaises(ObjNotReady) as err:
6499             seqof.encode()
6500         repr(err.exception)
6501         for i, value in enumerate(values):
6502             self.assertEqual(seqof[i], value)
6503             if not seqof[i].ready:
6504                 seqof[i] = Integer(i)
6505         self.assertTrue(seqof.ready)
6506         repr(seqof)
6507         list(seqof.pps())
6508         pprint(seqof, big_blobs=True, with_decode_path=True)
6509
6510     def test_spec_mismatch(self):
6511         class SeqOf(self.base_klass):
6512             schema = Integer()
6513         seqof = SeqOf()
6514         seqof.append(Integer(123))
6515         with self.assertRaises(ValueError):
6516             seqof.append(Boolean(False))
6517         with self.assertRaises(ValueError):
6518             seqof[0] = Boolean(False)
6519
6520     @given(data_strategy())
6521     def test_bounds_satisfied(self, d):
6522         class SeqOf(self.base_klass):
6523             schema = Boolean()
6524         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
6525         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6526         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
6527         SeqOf(value=value, bounds=(bound_min, bound_max))
6528
6529     @given(data_strategy())
6530     def test_bounds_unsatisfied(self, d):
6531         class SeqOf(self.base_klass):
6532             schema = Boolean()
6533         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
6534         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6535         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
6536         with self.assertRaises(BoundsError) as err:
6537             SeqOf(value=value, bounds=(bound_min, bound_max))
6538         repr(err.exception)
6539         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6540             SeqOf(bounds=(bound_min, bound_max)).decode(
6541                 SeqOf(value).encode()
6542             )
6543         repr(err.exception)
6544         value = [Boolean(True)] * d.draw(integers(
6545             min_value=bound_max + 1,
6546             max_value=bound_max + 10,
6547         ))
6548         with self.assertRaises(BoundsError) as err:
6549             SeqOf(value=value, bounds=(bound_min, bound_max))
6550         repr(err.exception)
6551         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6552             SeqOf(bounds=(bound_min, bound_max)).decode(
6553                 SeqOf(value).encode()
6554             )
6555         repr(err.exception)
6556
6557     @given(integers(min_value=1, max_value=10))
6558     def test_out_of_bounds(self, bound_max):
6559         class SeqOf(self.base_klass):
6560             schema = Integer()
6561             bounds = (0, bound_max)
6562         seqof = SeqOf()
6563         for _ in range(bound_max):
6564             seqof.append(Integer(123))
6565         with self.assertRaises(BoundsError):
6566             seqof.append(Integer(123))
6567
6568     @given(data_strategy())
6569     def test_call(self, d):
6570         (
6571             schema_initial,
6572             value_initial,
6573             bounds_initial,
6574             impl_initial,
6575             expl_initial,
6576             default_initial,
6577             optional_initial,
6578             _decoded_initial,
6579         ) = d.draw(seqof_values_strategy())
6580
6581         class SeqOf(self.base_klass):
6582             schema = schema_initial
6583         obj_initial = SeqOf(
6584             value=value_initial,
6585             bounds=bounds_initial,
6586             impl=impl_initial,
6587             expl=expl_initial,
6588             default=default_initial,
6589             optional=optional_initial or False,
6590             _decoded=_decoded_initial,
6591         )
6592         (
6593             _,
6594             value,
6595             bounds,
6596             impl,
6597             expl,
6598             default,
6599             optional,
6600             _decoded,
6601         ) = d.draw(seqof_values_strategy(
6602             schema=schema_initial,
6603             do_expl=impl_initial is None,
6604         ))
6605         if (default is None) and (obj_initial.default is not None):
6606             bounds = None
6607         if (
6608                 (bounds is None) and
6609                 (value is not None) and
6610                 (bounds_initial is not None) and
6611                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
6612         ):
6613             value = None
6614         if (
6615                 (bounds is None) and
6616                 (default is not None) and
6617                 (bounds_initial is not None) and
6618                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
6619         ):
6620             default = None
6621         obj = obj_initial(
6622             value=value,
6623             bounds=bounds,
6624             impl=impl,
6625             expl=expl,
6626             default=default,
6627             optional=optional,
6628         )
6629         if obj.ready:
6630             value_expected = default if value is None else value
6631             value_expected = (
6632                 default_initial if value_expected is None
6633                 else value_expected
6634             )
6635             value_expected = () if value_expected is None else value_expected
6636             self.assertEqual(obj, value_expected)
6637         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6638         self.assertEqual(obj.expl_tag, expl or expl_initial)
6639         self.assertEqual(
6640             obj.default,
6641             default_initial if default is None else default,
6642         )
6643         if obj.default is None:
6644             optional = optional_initial if optional is None else optional
6645             optional = False if optional is None else optional
6646         else:
6647             optional = True
6648         self.assertEqual(obj.optional, optional)
6649         self.assertEqual(
6650             (obj._bound_min, obj._bound_max),
6651             bounds or bounds_initial or (0, float("+inf")),
6652         )
6653
6654     @given(seqof_values_strategy())
6655     def test_copy(self, values):
6656         _schema, value, bounds, impl, expl, default, optional, _decoded = values
6657
6658         class SeqOf(self.base_klass):
6659             schema = _schema
6660         register_class(SeqOf)
6661         obj = SeqOf(
6662             value=value,
6663             bounds=bounds,
6664             impl=impl,
6665             expl=expl,
6666             default=default,
6667             optional=optional or False,
6668             _decoded=_decoded,
6669         )
6670         for copy_func in copy_funcs:
6671             obj_copied = copy_func(obj)
6672             self.assert_copied_basic_fields(obj, obj_copied)
6673             self.assertEqual(obj._bound_min, obj_copied._bound_min)
6674             self.assertEqual(obj._bound_max, obj_copied._bound_max)
6675             self.assertEqual(obj._value, obj_copied._value)
6676
6677     @given(
6678         lists(binary()),
6679         integers(min_value=1).map(tag_encode),
6680     )
6681     def test_stripped(self, values, tag_impl):
6682         class SeqOf(self.base_klass):
6683             schema = OctetString()
6684         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
6685         with self.assertRaises(NotEnoughData):
6686             obj.decode(obj.encode()[:-1])
6687
6688     @given(
6689         lists(binary()),
6690         integers(min_value=1).map(tag_ctxc),
6691     )
6692     def test_stripped_expl(self, values, tag_expl):
6693         class SeqOf(self.base_klass):
6694             schema = OctetString()
6695         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
6696         with self.assertRaises(NotEnoughData):
6697             obj.decode(obj.encode()[:-1])
6698
6699     @given(
6700         integers(min_value=31),
6701         integers(min_value=0),
6702         decode_path_strat,
6703     )
6704     def test_bad_tag(self, tag, offset, decode_path):
6705         with self.assertRaises(DecodeError) as err:
6706             self.base_klass().decode(
6707                 tag_encode(tag)[:-1],
6708                 offset=offset,
6709                 decode_path=decode_path,
6710             )
6711         repr(err.exception)
6712         self.assertEqual(err.exception.offset, offset)
6713         self.assertEqual(err.exception.decode_path, decode_path)
6714
6715     @given(
6716         integers(min_value=128),
6717         integers(min_value=0),
6718         decode_path_strat,
6719     )
6720     def test_bad_len(self, l, offset, decode_path):
6721         with self.assertRaises(DecodeError) as err:
6722             self.base_klass().decode(
6723                 self.base_klass.tag_default + len_encode(l)[:-1],
6724                 offset=offset,
6725                 decode_path=decode_path,
6726             )
6727         repr(err.exception)
6728         self.assertEqual(err.exception.offset, offset)
6729         self.assertEqual(err.exception.decode_path, decode_path)
6730
6731     @given(binary(min_size=1))
6732     def test_tag_mismatch(self, impl):
6733         assume(impl != self.base_klass.tag_default)
6734         with self.assertRaises(TagMismatch):
6735             self.base_klass(impl=impl).decode(self.base_klass().encode())
6736
6737     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6738     @given(
6739         seqof_values_strategy(schema=Integer()),
6740         lists(integers().map(Integer)),
6741         integers(min_value=1).map(tag_ctxc),
6742         integers(min_value=0),
6743         binary(max_size=5),
6744         decode_path_strat,
6745     )
6746     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
6747         _, _, _, _, _, default, optional, _decoded = values
6748
6749         class SeqOf(self.base_klass):
6750             schema = Integer()
6751         obj = SeqOf(
6752             value=value,
6753             default=default,
6754             optional=optional,
6755             _decoded=_decoded,
6756         )
6757         repr(obj)
6758         list(obj.pps())
6759         pprint(obj, big_blobs=True, with_decode_path=True)
6760         self.assertFalse(obj.expled)
6761         obj_encoded = obj.encode()
6762         obj_encoded_cer = encode_cer(obj)
6763         self.assertNotEqual(obj_encoded_cer, obj_encoded)
6764         self.assertSequenceEqual(
6765             obj.decod(obj_encoded_cer, ctx={"bered": True}).encode(),
6766             obj_encoded,
6767         )
6768         obj_expled = obj(value, expl=tag_expl)
6769         self.assertTrue(obj_expled.expled)
6770         repr(obj_expled)
6771         list(obj_expled.pps())
6772         pprint(obj_expled, big_blobs=True, with_decode_path=True)
6773         obj_expled_encoded = obj_expled.encode()
6774         ctx_copied = deepcopy(ctx_dummy)
6775         obj_decoded, tail = obj_expled.decode(
6776             obj_expled_encoded + tail_junk,
6777             offset=offset,
6778             ctx=ctx_copied,
6779         )
6780         self.assertDictEqual(ctx_copied, ctx_dummy)
6781         repr(obj_decoded)
6782         list(obj_decoded.pps())
6783         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
6784         self.assertEqual(tail, tail_junk)
6785         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
6786         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
6787         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
6788         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
6789         self.assertEqual(
6790             obj_decoded.expl_llen,
6791             len(len_encode(len(obj_encoded))),
6792         )
6793         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
6794         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
6795         self.assertEqual(
6796             obj_decoded.offset,
6797             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
6798         )
6799         self.assertEqual(obj_decoded.expl_offset, offset)
6800         for obj_inner in obj_decoded:
6801             self.assertIn(obj_inner, obj_decoded)
6802             self.assertSequenceEqual(
6803                 obj_inner.encode(),
6804                 obj_expled_encoded[
6805                     obj_inner.offset - offset:
6806                     obj_inner.offset + obj_inner.tlvlen - offset
6807                 ],
6808             )
6809
6810         t, _, lv = tag_strip(obj_encoded)
6811         _, _, v = len_decode(lv)
6812         obj_encoded_lenindef = t + LENINDEF + v + EOC
6813         with self.assertRaises(DecodeError):
6814             obj.decode(obj_encoded_lenindef)
6815         obj_decoded_lenindef, tail_lenindef = obj.decode(
6816             obj_encoded_lenindef + tail_junk,
6817             ctx={"bered": True},
6818         )
6819         self.assertTrue(obj_decoded_lenindef.lenindef)
6820         self.assertTrue(obj_decoded_lenindef.bered)
6821         obj_decoded_lenindef = copy(obj_decoded_lenindef)
6822         self.assertTrue(obj_decoded_lenindef.lenindef)
6823         self.assertTrue(obj_decoded_lenindef.bered)
6824         repr(obj_decoded_lenindef)
6825         list(obj_decoded_lenindef.pps())
6826         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
6827         self.assertEqual(tail_lenindef, tail_junk)
6828         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
6829         with self.assertRaises(DecodeError):
6830             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
6831         with self.assertRaises(DecodeError):
6832             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
6833
6834         evgens = list(obj.decode_evgen(
6835             obj_encoded_lenindef + tail_junk,
6836             decode_path=decode_path,
6837             ctx={"bered": True},
6838         ))
6839         self.assertEqual(len(evgens), len(obj_decoded_lenindef) + 1)
6840         for i, (_decode_path, obj, _) in enumerate(evgens[:-1]):
6841             self.assertEqual(_decode_path, decode_path + (str(i),))
6842             repr(obj)
6843             list(obj.pps())
6844         _decode_path, obj, tail = evgens[-1]
6845         self.assertEqual(_decode_path, decode_path)
6846         repr(obj)
6847         list(obj.pps())
6848
6849         assert_exceeding_data(
6850             self,
6851             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
6852             tail_junk,
6853         )
6854
6855     def test_bered(self):
6856         class SeqOf(self.base_klass):
6857             schema = Boolean()
6858         encoded = Boolean(False).encode()
6859         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
6860         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
6861         with self.assertRaises(DecodeError):
6862             SeqOf().decode(encoded)
6863         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
6864         self.assertFalse(decoded.ber_encoded)
6865         self.assertFalse(decoded.lenindef)
6866         self.assertTrue(decoded.bered)
6867         decoded = copy(decoded)
6868         self.assertFalse(decoded.ber_encoded)
6869         self.assertFalse(decoded.lenindef)
6870         self.assertTrue(decoded.bered)
6871
6872         class SeqOf(self.base_klass):
6873             schema = OctetString()
6874         encoded = OctetString(b"whatever").encode()
6875         encoded += (
6876             tag_encode(form=TagFormConstructed, num=4) +
6877             LENINDEF +
6878             OctetString(b"whatever").encode() +
6879             EOC
6880         )
6881         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
6882         with self.assertRaises(DecodeError):
6883             SeqOf().decode(encoded)
6884         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
6885         self.assertFalse(decoded.ber_encoded)
6886         self.assertFalse(decoded.lenindef)
6887         self.assertTrue(decoded.bered)
6888         decoded = copy(decoded)
6889         self.assertFalse(decoded.ber_encoded)
6890         self.assertFalse(decoded.lenindef)
6891         self.assertTrue(decoded.bered)
6892
6893
6894 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
6895     class SeqOf(SequenceOf):
6896         schema = "whatever"
6897     base_klass = SeqOf
6898
6899     def _test_symmetric_compare_objs(self, obj1, obj2):
6900         self.assertEqual(obj1, obj2)
6901         self.assertSequenceEqual(list(obj1), list(obj2))
6902
6903     def test_iterator_pickling(self):
6904         class SeqOf(SequenceOf):
6905             schema = Integer()
6906         register_class(SeqOf)
6907         seqof = SeqOf()
6908         pickle_dumps(seqof)
6909         seqof = seqof(iter(six_xrange(10)))
6910         with assertRaisesRegex(self, ValueError, "iterator"):
6911             pickle_dumps(seqof)
6912
6913     def test_iterator_bounds(self):
6914         class SeqOf(SequenceOf):
6915             schema = Integer()
6916             bounds = (10, 20)
6917         seqof = None
6918         def gen(n):
6919             for i in six_xrange(n):
6920                 yield Integer(i)
6921         for n in (9, 21):
6922             seqof = SeqOf(gen(n))
6923             self.assertTrue(seqof.ready)
6924             with self.assertRaises(BoundsError):
6925                 seqof.encode()
6926             self.assertFalse(seqof.ready)
6927             seqof = seqof(gen(n))
6928             self.assertTrue(seqof.ready)
6929             with self.assertRaises(BoundsError):
6930                 encode_cer(seqof)
6931             self.assertFalse(seqof.ready)
6932
6933     def test_iterator_twice(self):
6934         class SeqOf(SequenceOf):
6935             schema = Integer()
6936             bounds = (1, float("+inf"))
6937         def gen():
6938             for i in six_xrange(10):
6939                 yield Integer(i)
6940         seqof = SeqOf(gen())
6941         self.assertTrue(seqof.ready)
6942         seqof.encode()
6943         self.assertFalse(seqof.ready)
6944         register_class(SeqOf)
6945         pickle_dumps(seqof)
6946
6947     def test_non_ready_bound_min(self):
6948         class SeqOf(SequenceOf):
6949             schema = Integer()
6950             bounds = (1, float("+inf"))
6951         seqof = SeqOf()
6952         self.assertFalse(seqof.ready)
6953
6954
6955 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
6956     class SeqOf(SetOf):
6957         schema = "whatever"
6958     base_klass = SeqOf
6959
6960     def _test_symmetric_compare_objs(self, obj1, obj2):
6961         self.assertSetEqual(
6962             set(int(v) for v in obj1),
6963             set(int(v) for v in obj2),
6964         )
6965
6966     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6967     @given(data_strategy())
6968     def test_sorted(self, d):
6969         values = [OctetString(v) for v in d.draw(lists(binary()))]
6970
6971         class Seq(SetOf):
6972             schema = OctetString()
6973         seq = Seq(values)
6974         seq_encoded = seq.encode()
6975         seq_decoded, _ = seq.decode(seq_encoded)
6976         self.assertSequenceEqual(
6977             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6978             b"".join(sorted([v.encode() for v in values])),
6979         )
6980
6981     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6982     @given(data_strategy())
6983     def test_unsorted(self, d):
6984         values = [OctetString(v).encode() for v in d.draw(sets(
6985             binary(min_size=1, max_size=5),
6986             min_size=2,
6987             max_size=5,
6988         ))]
6989         values = d.draw(permutations(values))
6990         assume(values != sorted(values))
6991         encoded = b"".join(values)
6992         seq_encoded = b"".join((
6993             SetOf.tag_default,
6994             len_encode(len(encoded)),
6995             encoded,
6996         ))
6997
6998         class Seq(SetOf):
6999             schema = OctetString()
7000         seq = Seq()
7001         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
7002             seq.decode(seq_encoded)
7003
7004         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
7005             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
7006             self.assertTrue(seq_decoded.ber_encoded)
7007             self.assertTrue(seq_decoded.bered)
7008             seq_decoded = copy(seq_decoded)
7009             self.assertTrue(seq_decoded.ber_encoded)
7010             self.assertTrue(seq_decoded.bered)
7011             self.assertSequenceEqual(
7012                 [obj.encode() for obj in seq_decoded],
7013                 values,
7014             )
7015
7016
7017 class TestGoMarshalVectors(TestCase):
7018     def runTest(self):
7019         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
7020         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
7021         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
7022         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
7023         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
7024
7025         class Seq(Sequence):
7026             schema = (
7027                 ("erste", Integer()),
7028                 ("zweite", Integer(optional=True))
7029             )
7030         seq = Seq()
7031         seq["erste"] = Integer(64)
7032         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7033         seq["erste"] = Integer(0x123456)
7034         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
7035         seq["erste"] = Integer(64)
7036         seq["zweite"] = Integer(65)
7037         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
7038
7039         class NestedSeq(Sequence):
7040             schema = (
7041                 ("nest", Seq()),
7042             )
7043         seq["erste"] = Integer(127)
7044         seq["zweite"] = None
7045         nested = NestedSeq()
7046         nested["nest"] = seq
7047         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
7048
7049         self.assertSequenceEqual(
7050             OctetString(b"\x01\x02\x03").encode(),
7051             hexdec("0403010203"),
7052         )
7053
7054         class Seq(Sequence):
7055             schema = (
7056                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
7057             )
7058         seq = Seq()
7059         seq["erste"] = Integer(64)
7060         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
7061
7062         class Seq(Sequence):
7063             schema = (
7064                 ("erste", Integer(expl=tag_ctxc(5))),
7065             )
7066         seq = Seq()
7067         seq["erste"] = Integer(64)
7068         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
7069
7070         class Seq(Sequence):
7071             schema = (
7072                 ("erste", Null(
7073                     impl=tag_encode(0, klass=TagClassContext),
7074                     optional=True,
7075                 )),
7076             )
7077         seq = Seq()
7078         seq["erste"] = Null()
7079         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
7080         seq["erste"] = None
7081         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7082
7083         self.assertSequenceEqual(
7084             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
7085             hexdec("170d3730303130313030303030305a"),
7086         )
7087         self.assertSequenceEqual(
7088             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
7089             hexdec("170d3039313131353232353631365a"),
7090         )
7091         self.assertSequenceEqual(
7092             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
7093             hexdec("180f32313030303430353132303130315a"),
7094         )
7095
7096         class Seq(Sequence):
7097             schema = (
7098                 ("erste", GeneralizedTime()),
7099             )
7100         seq = Seq()
7101         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
7102         self.assertSequenceEqual(
7103             seq.encode(),
7104             hexdec("3011180f32303039313131353232353631365a"),
7105         )
7106
7107         self.assertSequenceEqual(
7108             BitString((1, b"\x80")).encode(),
7109             hexdec("03020780"),
7110         )
7111         self.assertSequenceEqual(
7112             BitString((12, b"\x81\xF0")).encode(),
7113             hexdec("03030481f0"),
7114         )
7115
7116         self.assertSequenceEqual(
7117             ObjectIdentifier("1.2.3.4").encode(),
7118             hexdec("06032a0304"),
7119         )
7120         self.assertSequenceEqual(
7121             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
7122             hexdec("06092a864888932d010105"),
7123         )
7124         self.assertSequenceEqual(
7125             ObjectIdentifier("2.100.3").encode(),
7126             hexdec("0603813403"),
7127         )
7128
7129         self.assertSequenceEqual(
7130             PrintableString("test").encode(),
7131             hexdec("130474657374"),
7132         )
7133         self.assertSequenceEqual(
7134             PrintableString("x" * 127).encode(),
7135             hexdec("137F" + "78" * 127),
7136         )
7137         self.assertSequenceEqual(
7138             PrintableString("x" * 128).encode(),
7139             hexdec("138180" + "78" * 128),
7140         )
7141         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
7142
7143         class Seq(Sequence):
7144             schema = (
7145                 ("erste", IA5String()),
7146             )
7147         seq = Seq()
7148         seq["erste"] = IA5String("test")
7149         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
7150
7151         class Seq(Sequence):
7152             schema = (
7153                 ("erste", PrintableString()),
7154             )
7155         seq = Seq()
7156         seq["erste"] = PrintableString("test")
7157         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
7158         # Asterisk is actually not allowable
7159         PrintableString._allowable_chars |= set(b"*")
7160         seq["erste"] = PrintableString("test*")
7161         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
7162         PrintableString._allowable_chars -= set(b"*")
7163
7164         class Seq(Sequence):
7165             schema = (
7166                 ("erste", Any(optional=True)),
7167                 ("zweite", Integer()),
7168             )
7169         seq = Seq()
7170         seq["zweite"] = Integer(64)
7171         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7172
7173         class Seq(SetOf):
7174             schema = Integer()
7175         seq = Seq()
7176         seq.append(Integer(10))
7177         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
7178
7179         class _SeqOf(SequenceOf):
7180             schema = PrintableString()
7181
7182         class SeqOf(SequenceOf):
7183             schema = _SeqOf()
7184         _seqof = _SeqOf()
7185         _seqof.append(PrintableString("1"))
7186         seqof = SeqOf()
7187         seqof.append(_seqof)
7188         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
7189
7190         class Seq(Sequence):
7191             schema = (
7192                 ("erste", Integer(default=1)),
7193             )
7194         seq = Seq()
7195         seq["erste"] = Integer(0)
7196         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
7197         seq["erste"] = Integer(1)
7198         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7199         seq["erste"] = Integer(2)
7200         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
7201
7202
7203 class TestPP(TestCase):
7204     @given(data_strategy())
7205     def test_oid_printing(self, d):
7206         oids = {
7207             str(ObjectIdentifier(k)): v * 2
7208             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
7209         }
7210         chosen = d.draw(sampled_from(sorted(oids)))
7211         chosen_id = oids[chosen]
7212         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
7213         self.assertNotIn(chosen_id, pp_console_row(pp))
7214         self.assertIn(
7215             chosen_id,
7216             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
7217         )
7218
7219
7220 class TestAutoAddSlots(TestCase):
7221     def runTest(self):
7222         class Inher(Integer):
7223             pass
7224
7225         with self.assertRaises(AttributeError):
7226             inher = Inher()
7227             inher.unexistent = "whatever"
7228
7229
7230 class TestOIDDefines(TestCase):
7231     @given(data_strategy())
7232     def runTest(self, d):
7233         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
7234         value_name_chosen = d.draw(sampled_from(value_names))
7235         oids = [
7236             ObjectIdentifier(oid)
7237             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
7238         ]
7239         oid_chosen = d.draw(sampled_from(oids))
7240         values = d.draw(lists(
7241             integers(),
7242             min_size=len(value_names),
7243             max_size=len(value_names),
7244         ))
7245         for definable_class in (Any, OctetString, BitString):
7246             _schema = [
7247                 ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
7248                     oid: Integer() for oid in oids[:-1]
7249                 }),))),
7250             ]
7251             for i, value_name in enumerate(value_names):
7252                 _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
7253
7254             class Seq(Sequence):
7255                 schema = _schema
7256             seq = Seq()
7257             for value_name, value in zip(value_names, values):
7258                 seq[value_name] = definable_class(Integer(value).encode())
7259             seq["type"] = oid_chosen
7260             seq, _ = Seq().decode(seq.encode())
7261             for value_name in value_names:
7262                 if value_name == value_name_chosen:
7263                     continue
7264                 self.assertIsNone(seq[value_name].defined)
7265             if value_name_chosen in oids[:-1]:
7266                 self.assertIsNotNone(seq[value_name_chosen].defined)
7267                 self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
7268                 self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
7269             repr(seq)
7270             list(seq.pps())
7271             pprint(seq, big_blobs=True, with_decode_path=True)
7272
7273
7274 class TestDefinesByPath(TestCase):
7275     def test_generated(self):
7276         class Seq(Sequence):
7277             schema = (
7278                 ("type", ObjectIdentifier()),
7279                 ("value", OctetString(expl=tag_ctxc(123))),
7280             )
7281
7282         class SeqInner(Sequence):
7283             schema = (
7284                 ("typeInner", ObjectIdentifier()),
7285                 ("valueInner", Any()),
7286             )
7287
7288         class PairValue(SetOf):
7289             schema = Any()
7290
7291         class Pair(Sequence):
7292             schema = (
7293                 ("type", ObjectIdentifier()),
7294                 ("value", PairValue()),
7295             )
7296
7297         class Pairs(SequenceOf):
7298             schema = Pair()
7299
7300         (
7301             type_integered,
7302             type_sequenced,
7303             type_innered,
7304             type_octet_stringed,
7305         ) = [
7306             ObjectIdentifier(oid)
7307             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
7308         ]
7309         seq_integered = Seq()
7310         seq_integered["type"] = type_integered
7311         seq_integered["value"] = OctetString(Integer(123).encode())
7312         seq_integered_raw = seq_integered.encode()
7313
7314         pairs = Pairs()
7315         pairs_input = (
7316             (type_octet_stringed, OctetString(b"whatever")),
7317             (type_integered, Integer(123)),
7318             (type_octet_stringed, OctetString(b"whenever")),
7319             (type_integered, Integer(234)),
7320         )
7321         for t, v in pairs_input:
7322             pairs.append(Pair((
7323                 ("type", t),
7324                 ("value", PairValue((Any(v),))),
7325             )))
7326         seq_inner = SeqInner()
7327         seq_inner["typeInner"] = type_innered
7328         seq_inner["valueInner"] = Any(pairs)
7329         seq_sequenced = Seq()
7330         seq_sequenced["type"] = type_sequenced
7331         seq_sequenced["value"] = OctetString(seq_inner.encode())
7332         seq_sequenced_raw = seq_sequenced.encode()
7333         repr(seq_sequenced)
7334         list(seq_sequenced.pps())
7335         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7336
7337         defines_by_path = []
7338         ctx_copied = deepcopy(ctx_dummy)
7339         seq_integered, _ = Seq().decode(
7340             seq_integered_raw,
7341             ctx=ctx_copied,
7342         )
7343         self.assertDictEqual(ctx_copied, ctx_dummy)
7344         self.assertIsNone(seq_integered["value"].defined)
7345         defines_by_path.append(
7346             (("type",), ((("value",), {
7347                 type_integered: Integer(),
7348                 type_sequenced: SeqInner(),
7349             }),))
7350         )
7351         ctx_copied["defines_by_path"] = defines_by_path
7352         seq_integered, _ = Seq().decode(
7353             seq_integered_raw,
7354             ctx=ctx_copied,
7355         )
7356         del ctx_copied["defines_by_path"]
7357         self.assertDictEqual(ctx_copied, ctx_dummy)
7358         self.assertIsNotNone(seq_integered["value"].defined)
7359         self.assertEqual(seq_integered["value"].defined[0], type_integered)
7360         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
7361         self.assertTrue(seq_integered_raw[
7362             seq_integered["value"].defined[1].offset:
7363         ].startswith(Integer(123).encode()))
7364         repr(seq_integered)
7365         list(seq_integered.pps())
7366         pprint(seq_integered, big_blobs=True, with_decode_path=True)
7367
7368         ctx_copied["defines_by_path"] = defines_by_path
7369         seq_sequenced, _ = Seq().decode(
7370             seq_sequenced_raw,
7371             ctx=ctx_copied,
7372         )
7373         del ctx_copied["defines_by_path"]
7374         self.assertDictEqual(ctx_copied, ctx_dummy)
7375         self.assertIsNotNone(seq_sequenced["value"].defined)
7376         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7377         seq_inner = seq_sequenced["value"].defined[1]
7378         self.assertIsNone(seq_inner["valueInner"].defined)
7379         repr(seq_sequenced)
7380         list(seq_sequenced.pps())
7381         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7382
7383         defines_by_path.append((
7384             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
7385             ((("valueInner",), {type_innered: Pairs()}),),
7386         ))
7387         ctx_copied["defines_by_path"] = defines_by_path
7388         seq_sequenced, _ = Seq().decode(
7389             seq_sequenced_raw,
7390             ctx=ctx_copied,
7391         )
7392         del ctx_copied["defines_by_path"]
7393         self.assertDictEqual(ctx_copied, ctx_dummy)
7394         self.assertIsNotNone(seq_sequenced["value"].defined)
7395         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7396         seq_inner = seq_sequenced["value"].defined[1]
7397         self.assertIsNotNone(seq_inner["valueInner"].defined)
7398         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7399         pairs = seq_inner["valueInner"].defined[1]
7400         for pair in pairs:
7401             self.assertIsNone(pair["value"][0].defined)
7402         repr(seq_sequenced)
7403         list(seq_sequenced.pps())
7404         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7405
7406         defines_by_path.append((
7407             (
7408                 "value",
7409                 DecodePathDefBy(type_sequenced),
7410                 "valueInner",
7411                 DecodePathDefBy(type_innered),
7412                 any,
7413                 "type",
7414             ),
7415             ((("value",), {
7416                 type_integered: Integer(),
7417                 type_octet_stringed: OctetString(),
7418             }),),
7419         ))
7420         ctx_copied["defines_by_path"] = defines_by_path
7421         seq_sequenced, _ = Seq().decode(
7422             seq_sequenced_raw,
7423             ctx=ctx_copied,
7424         )
7425         del ctx_copied["defines_by_path"]
7426         self.assertDictEqual(ctx_copied, ctx_dummy)
7427         self.assertIsNotNone(seq_sequenced["value"].defined)
7428         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7429         seq_inner = seq_sequenced["value"].defined[1]
7430         self.assertIsNotNone(seq_inner["valueInner"].defined)
7431         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7432         pairs_got = seq_inner["valueInner"].defined[1]
7433         for pair_input, pair_got in zip(pairs_input, pairs_got):
7434             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
7435             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
7436         repr(seq_sequenced)
7437         list(seq_sequenced.pps())
7438         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7439
7440     @given(oid_strategy(), integers())
7441     def test_simple(self, oid, tgt):
7442         class Inner(Sequence):
7443             schema = (
7444                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
7445                     ObjectIdentifier(oid): Integer(),
7446                 }),))),
7447             )
7448
7449         class Outer(Sequence):
7450             schema = (
7451                 ("inner", Inner()),
7452                 ("tgt", OctetString()),
7453             )
7454
7455         inner = Inner()
7456         inner["oid"] = ObjectIdentifier(oid)
7457         outer = Outer()
7458         outer["inner"] = inner
7459         outer["tgt"] = OctetString(Integer(tgt).encode())
7460         decoded, _ = Outer().decode(outer.encode())
7461         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
7462
7463     def test_remaining_data(self):
7464         oid = ObjectIdentifier("1.2.3")
7465         class Seq(Sequence):
7466             schema = (
7467                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7468                     oid: Integer(),
7469                 }),))),
7470                 ("tgt", OctetString()),
7471             )
7472
7473         seq = Seq((
7474             ("oid", oid),
7475             ("tgt", OctetString(Integer(123).encode() + b"junk")),
7476         ))
7477         with assertRaisesRegex(self, DecodeError, "remaining data"):
7478             Seq().decode(seq.encode())
7479
7480     def test_remaining_data_seqof(self):
7481         oid = ObjectIdentifier("1.2.3")
7482         class SeqOf(SetOf):
7483             schema = OctetString()
7484
7485         class Seq(Sequence):
7486             schema = (
7487                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7488                     oid: Integer(),
7489                 }),))),
7490                 ("tgt", SeqOf()),
7491             )
7492
7493         seq = Seq((
7494             ("oid", oid),
7495             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
7496         ))
7497         with assertRaisesRegex(self, DecodeError, "remaining data"):
7498             Seq().decode(seq.encode())
7499
7500
7501 class TestAbsDecodePath(TestCase):
7502     @given(
7503         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7504         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7505     )
7506     def test_concat(self, decode_path, rel_path):
7507         dp = abs_decode_path(decode_path, rel_path)
7508         self.assertSequenceEqual(dp, decode_path + rel_path)
7509         repr(dp)
7510
7511     @given(
7512         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7513         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7514     )
7515     def test_abs(self, decode_path, rel_path):
7516         self.assertSequenceEqual(
7517             abs_decode_path(decode_path, ("/",) + rel_path),
7518             rel_path,
7519         )
7520
7521     @given(
7522         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
7523         integers(min_value=1, max_value=3),
7524         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7525     )
7526     def test_dots(self, decode_path, number_of_dots, rel_path):
7527         self.assertSequenceEqual(
7528             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
7529             decode_path[:-number_of_dots] + rel_path,
7530         )
7531
7532
7533 class TestStrictDefaultExistence(TestCase):
7534     @given(data_strategy())
7535     def runTest(self, d):
7536         count = d.draw(integers(min_value=1, max_value=10))
7537         chosen = d.draw(integers(min_value=0, max_value=count - 1))
7538         _schema = [
7539             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
7540             for i in range(count)
7541         ]
7542         for klass in (Sequence, Set):
7543             class Seq(klass):
7544                 schema = _schema
7545             seq = Seq()
7546             for i in range(count):
7547                 seq["int%d" % i] = Integer(123)
7548             raw = seq.encode()
7549             chosen_choice = "int%d" % chosen
7550             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
7551             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
7552                 seq.decode(raw)
7553             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
7554             self.assertTrue(decoded.ber_encoded)
7555             self.assertTrue(decoded.bered)
7556             decoded = copy(decoded)
7557             self.assertTrue(decoded.ber_encoded)
7558             self.assertTrue(decoded.bered)
7559             decoded, _ = seq.decode(raw, ctx={"bered": True})
7560             self.assertTrue(decoded.ber_encoded)
7561             self.assertTrue(decoded.bered)
7562             decoded = copy(decoded)
7563             self.assertTrue(decoded.ber_encoded)
7564             self.assertTrue(decoded.bered)
7565
7566
7567 class TestX690PrefixedType(TestCase):
7568     def test_1(self):
7569         self.assertSequenceEqual(
7570             VisibleString("Jones").encode(),
7571             hexdec("1A054A6F6E6573"),
7572         )
7573
7574     def test_2(self):
7575         self.assertSequenceEqual(
7576             VisibleString(
7577                 "Jones",
7578                 impl=tag_encode(3, klass=TagClassApplication),
7579             ).encode(),
7580             hexdec("43054A6F6E6573"),
7581         )
7582
7583     def test_3(self):
7584         self.assertSequenceEqual(
7585             Any(
7586                 VisibleString(
7587                     "Jones",
7588                     impl=tag_encode(3, klass=TagClassApplication),
7589                 ),
7590                 expl=tag_ctxc(2),
7591             ).encode(),
7592             hexdec("A20743054A6F6E6573"),
7593         )
7594
7595     def test_4(self):
7596         self.assertSequenceEqual(
7597             OctetString(
7598                 VisibleString(
7599                     "Jones",
7600                     impl=tag_encode(3, klass=TagClassApplication),
7601                 ).encode(),
7602                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
7603             ).encode(),
7604             hexdec("670743054A6F6E6573"),
7605         )
7606
7607     def test_5(self):
7608         self.assertSequenceEqual(
7609             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
7610             hexdec("82054A6F6E6573"),
7611         )
7612
7613
7614 class TestExplOOB(TestCase):
7615     def runTest(self):
7616         expl = tag_ctxc(123)
7617         raw = Integer(123).encode() + Integer(234).encode()
7618         raw = b"".join((expl, len_encode(len(raw)), raw))
7619         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
7620             Integer(expl=expl).decode(raw)
7621         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
7622
7623
7624 class TestPickleDifferentVersion(TestCase):
7625     def runTest(self):
7626         pickled = pickle_dumps(Integer(123), pickle_proto)
7627         import pyderasn
7628         version_orig = pyderasn.__version__
7629         pyderasn.__version__ += "different"
7630         with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
7631             pickle_loads(pickled)
7632         pyderasn.__version__ = version_orig
7633         pickle_loads(pickled)
7634
7635
7636 class TestCERSetOrdering(TestCase):
7637     def test_vectors(self):
7638         """Taken from X.690-201508
7639         """
7640         class B(Choice):
7641             schema = (
7642                 ("c", Integer(impl=tag_ctxp(2))),
7643                 ("d", Integer(impl=tag_ctxp(4))),
7644             )
7645
7646         class F(Choice):
7647             schema = (
7648                 ("g", Integer(impl=tag_ctxp(5))),
7649                 ("h", Integer(impl=tag_ctxp(6))),
7650             )
7651
7652         class I(Choice):
7653             schema = (
7654                 ("j", Integer(impl=tag_ctxp(0))),
7655             )
7656
7657         class E(Choice):
7658             schema = (
7659                 ("f", F()),
7660                 ("i", I()),
7661             )
7662
7663         class A(Set):
7664             schema = (
7665                 ("a", Integer(impl=tag_ctxp(3))),
7666                 ("b", B(expl=tag_ctxc(1))),
7667                 ("e", E()),
7668             )
7669
7670         a = A((
7671             ("a", Integer(123)),
7672             ("b", B(("d", Integer(234)))),
7673             ("e", E(("f", F(("g", Integer(345)))))),
7674         ))
7675         order = sorted(a._values_for_encoding(), key=attrgetter("tag_order_cer"))
7676         self.assertSequenceEqual(
7677             [i.__class__.__name__ for i in order],
7678             ("E", "B", "Integer"),
7679         )