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