]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Use an updated hypothesis
[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-2022 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     )
2220     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
2221         for klass in (OctetString, OctetStringInherited):
2222             _, _, _, _, default, optional, _decoded = values
2223             obj = klass(
2224                 value=value,
2225                 default=default,
2226                 optional=optional,
2227                 _decoded=_decoded,
2228             )
2229             repr(obj)
2230             list(obj.pps())
2231             pprint(obj, big_blobs=True, with_decode_path=True)
2232             self.assertFalse(obj.expled)
2233             obj_encoded = obj.encode()
2234             self.assertEqual(encode2pass(obj), obj_encoded)
2235             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
2236             obj_expled = obj(value, expl=tag_expl)
2237             self.assertTrue(obj_expled.expled)
2238             repr(obj_expled)
2239             list(obj_expled.pps())
2240             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2241             obj_expled_encoded = obj_expled.encode()
2242             obj_expled_cer = encode_cer(obj_expled)
2243             self.assertNotEqual(obj_expled_cer, obj_encoded)
2244             self.assertSequenceEqual(
2245                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
2246                 obj_expled_encoded,
2247             )
2248             ctx_copied = deepcopy(ctx_dummy)
2249             obj_decoded, tail = obj_expled.decode(
2250                 obj_expled_encoded + tail_junk,
2251                 offset=offset,
2252                 ctx=ctx_copied,
2253             )
2254             self.assertDictEqual(ctx_copied, ctx_dummy)
2255             repr(obj_decoded)
2256             list(obj_decoded.pps())
2257             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2258             self.assertEqual(tail, tail_junk)
2259             self.assertEqual(obj_decoded, obj_expled)
2260             self.assertNotEqual(obj_decoded, obj)
2261             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
2262             self.assertEqual(bytes(obj_decoded), bytes(obj))
2263             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2264             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2265             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2266             self.assertEqual(
2267                 obj_decoded.expl_llen,
2268                 len(len_encode(len(obj_encoded))),
2269             )
2270             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2271             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2272             self.assertEqual(
2273                 obj_decoded.offset,
2274                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2275             )
2276             self.assertEqual(obj_decoded.expl_offset, offset)
2277             assert_exceeding_data(
2278                 self,
2279                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2280                 tail_junk,
2281             )
2282
2283             evgens = list(obj_expled.decode_evgen(
2284                 obj_expled_encoded + tail_junk,
2285                 offset=offset,
2286                 decode_path=decode_path,
2287                 ctx=ctx_copied,
2288             ))
2289             self.assertEqual(len(evgens), 1)
2290             _decode_path, obj, tail = evgens[0]
2291             self.assertSequenceEqual(tail, tail_junk)
2292             self.assertEqual(_decode_path, decode_path)
2293             self.assertEqual(obj.expl_offset, offset)
2294             repr(obj)
2295             list(obj.pps())
2296
2297     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2298     @given(
2299         integers(min_value=1, max_value=30),
2300         lists(
2301             one_of(
2302                 binary(min_size=1, max_size=5),
2303                 lists(
2304                     binary(min_size=1, max_size=5),
2305                     min_size=1,
2306                     max_size=3,
2307                 ),
2308             ),
2309             min_size=1,
2310             max_size=3,
2311         ),
2312         binary(),
2313         decode_path_strat,
2314     )
2315     def test_constructed(self, impl, chunk_inputs, junk, decode_path):
2316         def chunk_constructed(contents):
2317             return (
2318                 tag_encode(form=TagFormConstructed, num=4) +
2319                 LENINDEF +
2320                 b"".join(OctetString(content).encode() for content in contents) +
2321                 EOC
2322             )
2323         chunks = []
2324         chunks_len_expected = []
2325         payload_expected = b""
2326         for chunk_input in chunk_inputs:
2327             if isinstance(chunk_input, bytes):
2328                 chunks.append(OctetString(chunk_input).encode())
2329                 payload_expected += chunk_input
2330                 chunks_len_expected.append(len(chunk_input))
2331             else:
2332                 chunks.append(chunk_constructed(chunk_input))
2333                 payload = b"".join(chunk_input)
2334                 payload_expected += payload
2335                 for c in chunk_input:
2336                     chunks_len_expected.append(len(c))
2337                 chunks_len_expected.append(len(chunks[-1]) - 1 - 1)
2338         encoded_indefinite = (
2339             tag_encode(form=TagFormConstructed, num=impl) +
2340             LENINDEF +
2341             b"".join(chunks) +
2342             EOC
2343         )
2344         encoded_definite = (
2345             tag_encode(form=TagFormConstructed, num=impl) +
2346             len_encode(len(b"".join(chunks))) +
2347             b"".join(chunks)
2348         )
2349         with self.assertRaisesRegex(DecodeError, "unallowed BER"):
2350             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
2351         for lenindef_expected, encoded in (
2352                 (True, encoded_indefinite),
2353                 (False, encoded_definite),
2354         ):
2355             obj, tail = OctetString(impl=tag_encode(impl)).decode(
2356                 encoded + junk,
2357                 ctx={"bered": True},
2358             )
2359             self.assertSequenceEqual(tail, junk)
2360             self.assertSequenceEqual(bytes(obj), payload_expected)
2361             self.assertTrue(obj.ber_encoded)
2362             self.assertEqual(obj.lenindef, lenindef_expected)
2363             self.assertTrue(obj.bered)
2364             obj = copy(obj)
2365             self.assertTrue(obj.ber_encoded)
2366             self.assertEqual(obj.lenindef, lenindef_expected)
2367             self.assertTrue(obj.bered)
2368             self.assertEqual(len(encoded), obj.tlvlen)
2369             repr(obj)
2370             list(obj.pps())
2371             pprint(obj, big_blobs=True, with_decode_path=True)
2372
2373             evgens = list(OctetString(impl=tag_encode(impl)).decode_evgen(
2374                 encoded,
2375                 decode_path=decode_path,
2376                 ctx={"bered": True},
2377             ))
2378             self.assertEqual(len(evgens), len(chunks_len_expected) + 1)
2379             for chunk_len_expected, (dp, obj, _) in zip(chunks_len_expected, evgens):
2380                 self.assertGreater(len(dp), len(decode_path))
2381                 self.assertEqual(obj.vlen, chunk_len_expected)
2382
2383     @given(
2384         integers(min_value=0),
2385         decode_path_strat,
2386     )
2387     def test_ber_definite_too_short(self, offset, decode_path):
2388         with self.assertRaisesRegex(DecodeError, "longer than data") as err:
2389             OctetString().decode(
2390                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
2391                 offset=offset,
2392                 decode_path=decode_path,
2393                 ctx={"bered": True},
2394             )
2395         self.assertEqual(err.exception.decode_path, decode_path)
2396         self.assertEqual(err.exception.offset, offset)
2397
2398     @given(
2399         integers(min_value=0),
2400         decode_path_strat,
2401         integers(min_value=1, max_value=3),
2402     )
2403     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
2404         bs = OctetString(b"data").encode()
2405         with self.assertRaises(NotEnoughData) as err:
2406             OctetString().decode(
2407                 tag_encode(4, form=TagFormConstructed) + LENINDEF + chunks * bs,
2408                 offset=offset,
2409                 decode_path=decode_path,
2410                 ctx={"bered": True},
2411             )
2412         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2413         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2414
2415     @given(
2416         integers(min_value=0),
2417         decode_path_strat,
2418         integers(min_value=1, max_value=3),
2419     )
2420     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
2421         bs = OctetString(b"data").encode()
2422         bs_longer = OctetString(b"data-longer").encode()
2423         with self.assertRaisesRegex(DecodeError, "chunk out of bounds") as err:
2424             OctetString().decode(
2425                 (
2426                     tag_encode(4, form=TagFormConstructed) +
2427                     len_encode((chunks + 1) * len(bs)) +
2428                     chunks * bs +
2429                     bs_longer
2430                 ),
2431                 offset=offset,
2432                 decode_path=decode_path,
2433                 ctx={"bered": True},
2434             )
2435         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2436         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2437
2438     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2439     @given(integers(min_value=1001, max_value=3000))
2440     def test_cer(self, data_len):
2441         data = urandom(data_len)
2442         encoded = encode_cer(OctetString(data))
2443         ctx = {"bered": True}
2444         self.assertSequenceEqual(bytes(OctetString().decod(encoded, ctx=ctx)), data)
2445         evgens = list(OctetString().decode_evgen(encoded, ctx=ctx))
2446         evgens_expected = data_len // 1000
2447         if evgens_expected * 1000 != data_len:
2448             evgens_expected += 1
2449         evgens_expected += 1
2450         self.assertEqual(len(evgens), evgens_expected)
2451         for (_, obj, _) in evgens[:-2]:
2452             self.assertEqual(obj.vlen, 1000)
2453         _, obj, _ = evgens[-2]
2454         self.assertEqual(obj.vlen, data_len - len(evgens[:-2]) * 1000)
2455
2456
2457 @composite
2458 def null_values_strategy(draw, do_expl=False):
2459     impl = None
2460     expl = None
2461     if do_expl:
2462         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2463     else:
2464         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2465     optional = draw(one_of(none(), booleans()))
2466     _decoded = (
2467         draw(integers(min_value=0)),
2468         draw(integers(min_value=0)),
2469         draw(integers(min_value=0)),
2470     )
2471     return (impl, expl, optional, _decoded)
2472
2473
2474 class NullInherited(Null):
2475     pass
2476
2477
2478 class TestNull(CommonMixin, TestCase):
2479     base_klass = Null
2480
2481     def test_ready(self):
2482         obj = Null()
2483         self.assertTrue(obj.ready)
2484         repr(obj)
2485         list(obj.pps())
2486         pprint(obj, big_blobs=True, with_decode_path=True)
2487
2488     @given(binary(min_size=1), binary(min_size=1))
2489     def test_comparison(self, tag1, tag2):
2490         for klass in (Null, NullInherited):
2491             obj1 = klass(impl=tag1)
2492             obj2 = klass(impl=tag2)
2493             self.assertEqual(obj1 == obj2, tag1 == tag2)
2494             self.assertEqual(obj1 != obj2, tag1 != tag2)
2495             self.assertNotEqual(obj1, tag2)
2496
2497     @given(data_strategy())
2498     def test_call(self, d):
2499         for klass in (Null, NullInherited):
2500             (
2501                 impl_initial,
2502                 expl_initial,
2503                 optional_initial,
2504                 _decoded_initial,
2505             ) = d.draw(null_values_strategy())
2506             obj_initial = klass(
2507                 impl=impl_initial,
2508                 expl=expl_initial,
2509                 optional=optional_initial or False,
2510                 _decoded=_decoded_initial,
2511             )
2512             (
2513                 impl,
2514                 expl,
2515                 optional,
2516                 _decoded,
2517             ) = d.draw(null_values_strategy(do_expl=impl_initial is None))
2518             obj = obj_initial(impl=impl, expl=expl, optional=optional)
2519             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2520             self.assertEqual(obj.expl_tag, expl or expl_initial)
2521             optional = optional_initial if optional is None else optional
2522             optional = False if optional is None else optional
2523             self.assertEqual(obj.optional, optional)
2524
2525     @given(null_values_strategy())
2526     def test_copy(self, values):
2527         for klass in (Null, NullInherited):
2528             impl, expl, optional, _decoded = values
2529             obj = klass(
2530                 impl=impl,
2531                 expl=expl,
2532                 optional=optional or False,
2533                 _decoded=_decoded,
2534             )
2535             for copy_func in copy_funcs:
2536                 obj_copied = copy_func(obj)
2537                 self.assert_copied_basic_fields(obj, obj_copied)
2538
2539     @given(integers(min_value=1).map(tag_encode))
2540     def test_stripped(self, tag_impl):
2541         obj = Null(impl=tag_impl)
2542         with self.assertRaises(NotEnoughData):
2543             obj.decode(obj.encode()[:-1])
2544
2545     @given(integers(min_value=1).map(tag_ctxc))
2546     def test_stripped_expl(self, tag_expl):
2547         obj = Null(expl=tag_expl)
2548         with self.assertRaises(NotEnoughData):
2549             obj.decode(obj.encode()[:-1])
2550
2551     @given(
2552         integers(min_value=31),
2553         integers(min_value=0),
2554         decode_path_strat,
2555     )
2556     def test_bad_tag(self, tag, offset, decode_path):
2557         with self.assertRaises(DecodeError) as err:
2558             Null().decode(
2559                 tag_encode(tag)[:-1],
2560                 offset=offset,
2561                 decode_path=decode_path,
2562             )
2563         repr(err.exception)
2564         self.assertEqual(err.exception.offset, offset)
2565         self.assertEqual(err.exception.decode_path, decode_path)
2566
2567     @given(
2568         integers(min_value=128),
2569         integers(min_value=0),
2570         decode_path_strat,
2571     )
2572     def test_bad_len(self, l, offset, decode_path):
2573         with self.assertRaises(DecodeError) as err:
2574             Null().decode(
2575                 Null.tag_default + len_encode(l)[:-1],
2576                 offset=offset,
2577                 decode_path=decode_path,
2578             )
2579         repr(err.exception)
2580         self.assertEqual(err.exception.offset, offset)
2581         self.assertEqual(err.exception.decode_path, decode_path)
2582
2583     @given(binary(min_size=1))
2584     def test_tag_mismatch(self, impl):
2585         assume(impl != Null.tag_default)
2586         with self.assertRaises(TagMismatch):
2587             Null(impl=impl).decode(Null().encode())
2588
2589     @given(
2590         null_values_strategy(),
2591         integers(min_value=1).map(tag_ctxc),
2592         integers(min_value=0),
2593         binary(max_size=5),
2594         decode_path_strat,
2595     )
2596     def test_symmetric(self, values, tag_expl, offset, tail_junk, decode_path):
2597         for klass in (Null, NullInherited):
2598             _, _, optional, _decoded = values
2599             obj = klass(optional=optional, _decoded=_decoded)
2600             repr(obj)
2601             list(obj.pps())
2602             pprint(obj, big_blobs=True, with_decode_path=True)
2603             self.assertFalse(obj.expled)
2604             obj_encoded = obj.encode()
2605             self.assertEqual(encode2pass(obj), obj_encoded)
2606             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
2607             obj_expled = obj(expl=tag_expl)
2608             self.assertTrue(obj_expled.expled)
2609             repr(obj_expled)
2610             list(obj_expled.pps())
2611             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2612             obj_expled_encoded = obj_expled.encode()
2613             obj_expled_cer = encode_cer(obj_expled)
2614             self.assertNotEqual(obj_expled_cer, obj_encoded)
2615             self.assertSequenceEqual(
2616                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
2617                 obj_expled_encoded,
2618             )
2619             ctx_copied = deepcopy(ctx_dummy)
2620             obj_decoded, tail = obj_expled.decode(
2621                 obj_expled_encoded + tail_junk,
2622                 offset=offset,
2623                 ctx=ctx_copied,
2624             )
2625             self.assertDictEqual(ctx_copied, ctx_dummy)
2626             repr(obj_decoded)
2627             list(obj_decoded.pps())
2628             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
2629             self.assertEqual(tail, tail_junk)
2630             self.assertEqual(obj_decoded, obj_expled)
2631             self.assertNotEqual(obj_decoded, obj)
2632             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2633             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2634             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2635             self.assertEqual(
2636                 obj_decoded.expl_llen,
2637                 len(len_encode(len(obj_encoded))),
2638             )
2639             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2640             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2641             self.assertEqual(
2642                 obj_decoded.offset,
2643                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2644             )
2645             self.assertEqual(obj_decoded.expl_offset, offset)
2646             assert_exceeding_data(
2647                 self,
2648                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
2649                 tail_junk,
2650             )
2651
2652             evgens = list(obj_expled.decode_evgen(
2653                 obj_expled_encoded + tail_junk,
2654                 offset=offset,
2655                 decode_path=decode_path,
2656                 ctx=ctx_copied,
2657             ))
2658             self.assertEqual(len(evgens), 1)
2659             _decode_path, obj, tail = evgens[0]
2660             self.assertSequenceEqual(tail, tail_junk)
2661             self.assertEqual(_decode_path, decode_path)
2662             self.assertEqual(obj, obj_decoded)
2663             self.assertEqual(obj.expl_offset, offset)
2664             repr(obj)
2665             list(obj.pps())
2666
2667     @given(integers(min_value=1))
2668     def test_invalid_len(self, l):
2669         with self.assertRaises(InvalidLength):
2670             Null().decode(b"".join((
2671                 Null.tag_default,
2672                 len_encode(l),
2673             )))
2674
2675
2676 @composite
2677 def oid_strategy(draw):
2678     first_arc = draw(integers(min_value=0, max_value=2))
2679     second_arc = 0
2680     if first_arc in (0, 1):
2681         second_arc = draw(integers(min_value=0, max_value=39))
2682     else:
2683         second_arc = draw(integers(min_value=0, max_value=1 << 63))
2684     other_arcs = draw(lists(integers(min_value=0, max_value=1 << 63)))
2685     return tuple([first_arc, second_arc] + other_arcs)
2686
2687
2688 @composite
2689 def oid_values_strategy(draw, do_expl=False):
2690     value = draw(one_of(none(), oid_strategy()))
2691     impl = None
2692     expl = None
2693     if do_expl:
2694         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2695     else:
2696         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2697     default = draw(one_of(none(), oid_strategy()))
2698     optional = draw(one_of(none(), booleans()))
2699     _decoded = (
2700         draw(integers(min_value=0)),
2701         draw(integers(min_value=0)),
2702         draw(integers(min_value=0)),
2703     )
2704     return (value, impl, expl, default, optional, _decoded)
2705
2706
2707 class ObjectIdentifierInherited(ObjectIdentifier):
2708     pass
2709
2710
2711 class TestObjectIdentifier(CommonMixin, TestCase):
2712     base_klass = ObjectIdentifier
2713
2714     def test_invalid_value_type(self):
2715         with self.assertRaises(InvalidValueType) as err:
2716             ObjectIdentifier(123)
2717         repr(err.exception)
2718
2719     @given(booleans())
2720     def test_optional(self, optional):
2721         obj = ObjectIdentifier(default=ObjectIdentifier("1.2.3"), optional=optional)
2722         self.assertTrue(obj.optional)
2723
2724     @given(oid_strategy())
2725     def test_ready(self, value):
2726         obj = ObjectIdentifier()
2727         self.assertFalse(obj.ready)
2728         repr(obj)
2729         list(obj.pps())
2730         pprint(obj, big_blobs=True, with_decode_path=True)
2731         with self.assertRaises(ObjNotReady) as err:
2732             obj.encode()
2733         repr(err.exception)
2734         with self.assertRaises(ObjNotReady) as err:
2735             encode2pass(obj)
2736         obj = ObjectIdentifier(value)
2737         self.assertTrue(obj.ready)
2738         self.assertFalse(obj.ber_encoded)
2739         repr(obj)
2740         list(obj.pps())
2741         pprint(obj, big_blobs=True, with_decode_path=True)
2742         hash(obj)
2743
2744     @given(oid_strategy(), oid_strategy(), binary(min_size=1), binary(min_size=1))
2745     def test_comparison(self, value1, value2, tag1, tag2):
2746         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2747             obj1 = klass(value1)
2748             obj2 = klass(value2)
2749             self.assertEqual(obj1 == obj2, value1 == value2)
2750             self.assertEqual(obj1 != obj2, value1 != value2)
2751             self.assertEqual(obj1 == tuple(obj2), value1 == value2)
2752             self.assertEqual(str(obj1) == str(obj2), value1 == value2)
2753             obj1 = klass(value1, impl=tag1)
2754             obj2 = klass(value1, impl=tag2)
2755             self.assertEqual(obj1 == obj2, tag1 == tag2)
2756             self.assertEqual(obj1 != obj2, tag1 != tag2)
2757
2758     @given(lists(oid_strategy()))
2759     def test_sorted_works(self, values):
2760         self.assertSequenceEqual(
2761             [tuple(v) for v in sorted(ObjectIdentifier(v) for v in values)],
2762             sorted(values),
2763         )
2764
2765     @given(data_strategy())
2766     def test_call(self, d):
2767         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2768             (
2769                 value_initial,
2770                 impl_initial,
2771                 expl_initial,
2772                 default_initial,
2773                 optional_initial,
2774                 _decoded_initial,
2775             ) = d.draw(oid_values_strategy())
2776             obj_initial = klass(
2777                 value=value_initial,
2778                 impl=impl_initial,
2779                 expl=expl_initial,
2780                 default=default_initial,
2781                 optional=optional_initial or False,
2782                 _decoded=_decoded_initial,
2783             )
2784             (
2785                 value,
2786                 impl,
2787                 expl,
2788                 default,
2789                 optional,
2790                 _decoded,
2791             ) = d.draw(oid_values_strategy(do_expl=impl_initial is None))
2792             obj = obj_initial(
2793                 value=value,
2794                 impl=impl,
2795                 expl=expl,
2796                 default=default,
2797                 optional=optional,
2798             )
2799             if obj.ready:
2800                 value_expected = default if value is None else value
2801                 value_expected = (
2802                     default_initial if value_expected is None
2803                     else value_expected
2804                 )
2805                 self.assertEqual(obj, value_expected)
2806             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2807             self.assertEqual(obj.expl_tag, expl or expl_initial)
2808             self.assertEqual(
2809                 obj.default,
2810                 default_initial if default is None else default,
2811             )
2812             if obj.default is None:
2813                 optional = optional_initial if optional is None else optional
2814                 optional = False if optional is None else optional
2815             else:
2816                 optional = True
2817             self.assertEqual(obj.optional, optional)
2818
2819     @given(oid_values_strategy())
2820     def test_copy(self, values):
2821         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2822             (
2823                 value,
2824                 impl,
2825                 expl,
2826                 default,
2827                 optional,
2828                 _decoded,
2829             ) = values
2830             obj = klass(
2831                 value=value,
2832                 impl=impl,
2833                 expl=expl,
2834                 default=default,
2835                 optional=optional,
2836                 _decoded=_decoded,
2837             )
2838             for copy_func in copy_funcs:
2839                 obj_copied = copy_func(obj)
2840                 self.assert_copied_basic_fields(obj, obj_copied)
2841                 self.assertEqual(obj._value, obj_copied._value)
2842
2843     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2844     @given(
2845         oid_strategy(),
2846         integers(min_value=1).map(tag_encode),
2847     )
2848     def test_stripped(self, value, tag_impl):
2849         obj = ObjectIdentifier(value, impl=tag_impl)
2850         with self.assertRaises(NotEnoughData):
2851             obj.decode(obj.encode()[:-1])
2852
2853     @given(
2854         oid_strategy(),
2855         integers(min_value=1).map(tag_ctxc),
2856     )
2857     def test_stripped_expl(self, value, tag_expl):
2858         obj = ObjectIdentifier(value, expl=tag_expl)
2859         with self.assertRaises(NotEnoughData):
2860             obj.decode(obj.encode()[:-1])
2861
2862     @given(
2863         integers(min_value=31),
2864         integers(min_value=0),
2865         decode_path_strat,
2866     )
2867     def test_bad_tag(self, tag, offset, decode_path):
2868         with self.assertRaises(DecodeError) as err:
2869             ObjectIdentifier().decode(
2870                 tag_encode(tag)[:-1],
2871                 offset=offset,
2872                 decode_path=decode_path,
2873             )
2874         repr(err.exception)
2875         self.assertEqual(err.exception.offset, offset)
2876         self.assertEqual(err.exception.decode_path, decode_path)
2877
2878     @given(
2879         integers(min_value=128),
2880         integers(min_value=0),
2881         decode_path_strat,
2882     )
2883     def test_bad_len(self, l, offset, decode_path):
2884         with self.assertRaises(DecodeError) as err:
2885             ObjectIdentifier().decode(
2886                 ObjectIdentifier.tag_default + len_encode(l)[:-1],
2887                 offset=offset,
2888                 decode_path=decode_path,
2889             )
2890         repr(err.exception)
2891         self.assertEqual(err.exception.offset, offset)
2892         self.assertEqual(err.exception.decode_path, decode_path)
2893
2894     def test_zero_oid(self):
2895         with self.assertRaises(NotEnoughData):
2896             ObjectIdentifier().decode(
2897                 b"".join((ObjectIdentifier.tag_default, len_encode(0)))
2898             )
2899
2900     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2901     @given(oid_strategy())
2902     def test_unfinished_oid(self, value):
2903         assume(list(value)[-1] > 255)
2904         obj_encoded = ObjectIdentifier(value).encode()
2905         obj, _ = ObjectIdentifier().decode(obj_encoded)
2906         data = obj_encoded[obj.tlen + obj.llen:-1]
2907         data = b"".join((
2908             ObjectIdentifier.tag_default,
2909             len_encode(len(data)),
2910             data,
2911         ))
2912         with self.assertRaisesRegex(DecodeError, "unfinished OID"):
2913             obj.decode(data)
2914
2915     @given(integers(min_value=0, max_value=1 << 63))
2916     def test_invalid_short(self, value):
2917         with self.assertRaises(InvalidOID):
2918             ObjectIdentifier((value,))
2919         with self.assertRaises(InvalidOID):
2920             ObjectIdentifier("%d" % value)
2921
2922     @given(
2923         integers(min_value=3, max_value=1 << 63),
2924         integers(min_value=0, max_value=1 << 63),
2925     )
2926     def test_invalid_first_arc(self, first_arc, second_arc):
2927         with self.assertRaises(InvalidOID):
2928             ObjectIdentifier((first_arc, second_arc))
2929         with self.assertRaises(InvalidOID):
2930             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2931
2932     @given(
2933         integers(min_value=0, max_value=1),
2934         integers(min_value=40, max_value=1 << 63),
2935     )
2936     def test_invalid_second_arc(self, first_arc, second_arc):
2937         with self.assertRaises(InvalidOID):
2938             ObjectIdentifier((first_arc, second_arc))
2939         with self.assertRaises(InvalidOID):
2940             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2941
2942     @given(text(alphabet=ascii_letters + ".", min_size=1))
2943     def test_junk(self, oid):
2944         with self.assertRaises(InvalidOID):
2945             ObjectIdentifier(oid)
2946
2947     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2948     @given(oid_strategy())
2949     def test_validness(self, oid):
2950         obj = ObjectIdentifier(oid)
2951         self.assertEqual(obj, ObjectIdentifier(".".join(str(arc) for arc in oid)))
2952         str(obj)
2953         repr(obj)
2954         pprint(obj, big_blobs=True, with_decode_path=True)
2955
2956     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2957     @given(
2958         oid_values_strategy(),
2959         oid_strategy(),
2960         integers(min_value=1).map(tag_ctxc),
2961         integers(min_value=0),
2962         binary(max_size=5),
2963         decode_path_strat,
2964     )
2965     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
2966         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2967             _, _, _, default, optional, _decoded = values
2968             obj = klass(
2969                 value=value,
2970                 default=default,
2971                 optional=optional,
2972                 _decoded=_decoded,
2973             )
2974             repr(obj)
2975             list(obj.pps())
2976             pprint(obj, big_blobs=True, with_decode_path=True)
2977             self.assertFalse(obj.expled)
2978             obj_encoded = obj.encode()
2979             self.assertEqual(encode2pass(obj), obj_encoded)
2980             self.assertSequenceEqual(encode_cer(obj), obj_encoded)
2981             obj_expled = obj(value, expl=tag_expl)
2982             self.assertTrue(obj_expled.expled)
2983             repr(obj_expled)
2984             list(obj_expled.pps())
2985             pprint(obj_expled, big_blobs=True, with_decode_path=True)
2986             obj_expled_encoded = obj_expled.encode()
2987             obj_expled_cer = encode_cer(obj_expled)
2988             self.assertNotEqual(obj_expled_cer, obj_encoded)
2989             self.assertSequenceEqual(
2990                 obj_expled.decod(obj_expled_cer, ctx={"bered": True}).encode(),
2991                 obj_expled_encoded,
2992             )
2993             ctx_copied = deepcopy(ctx_dummy)
2994             obj_decoded, tail = obj_expled.decode(
2995                 obj_expled_encoded + tail_junk,
2996                 offset=offset,
2997                 ctx=ctx_copied,
2998             )
2999             self.assertDictEqual(ctx_copied, ctx_dummy)
3000             repr(obj_decoded)
3001             list(obj_decoded.pps())
3002             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3003             self.assertEqual(tail, tail_junk)
3004             self.assertEqual(obj_decoded, obj_expled)
3005             self.assertNotEqual(obj_decoded, obj)
3006             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
3007             self.assertEqual(tuple(obj_decoded), tuple(obj))
3008             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3009             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3010             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3011             self.assertEqual(
3012                 obj_decoded.expl_llen,
3013                 len(len_encode(len(obj_encoded))),
3014             )
3015             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3016             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3017             self.assertEqual(
3018                 obj_decoded.offset,
3019                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3020             )
3021             self.assertEqual(obj_decoded.expl_offset, offset)
3022             assert_exceeding_data(
3023                 self,
3024                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3025                 tail_junk,
3026             )
3027
3028             evgens = list(obj_expled.decode_evgen(
3029                 obj_expled_encoded + tail_junk,
3030                 offset=offset,
3031                 decode_path=decode_path,
3032                 ctx=ctx_copied,
3033             ))
3034             self.assertEqual(len(evgens), 1)
3035             _decode_path, obj, tail = evgens[0]
3036             self.assertSequenceEqual(tail, tail_junk)
3037             self.assertEqual(_decode_path, decode_path)
3038             self.assertEqual(obj, obj_decoded)
3039             self.assertEqual(obj.expl_offset, offset)
3040             repr(obj)
3041             list(obj.pps())
3042
3043     @given(
3044         oid_strategy().map(ObjectIdentifier),
3045         oid_strategy().map(ObjectIdentifier),
3046     )
3047     def test_add(self, oid1, oid2):
3048         oid_expect = ObjectIdentifier(str(oid1) + "." + str(oid2))
3049         for oid_to_add in (oid2, tuple(oid2)):
3050             self.assertEqual(oid1 + oid_to_add, oid_expect)
3051         with self.assertRaises(InvalidValueType):
3052             oid1 + str(oid2)
3053
3054     def test_go_vectors_valid(self):
3055         for data, expect in (
3056                 (b"\x55", (2, 5)),
3057                 (b"\x55\x02", (2, 5, 2)),
3058                 (b"\x55\x02\xc0\x00", (2, 5, 2, 8192)),
3059                 (b"\x81\x34\x03", (2, 100, 3)),
3060         ):
3061             self.assertEqual(
3062                 ObjectIdentifier().decode(b"".join((
3063                     ObjectIdentifier.tag_default,
3064                     len_encode(len(data)),
3065                     data,
3066                 )))[0],
3067                 expect,
3068             )
3069
3070     def test_go_vectors_invalid(self):
3071         data = b"\x55\x02\xc0\x80\x80\x80\x80"
3072         with self.assertRaises(DecodeError):
3073             ObjectIdentifier().decode(b"".join((
3074                 Integer.tag_default,
3075                 len_encode(len(data)),
3076                 data,
3077             )))
3078
3079     def test_go_non_minimal_encoding(self):
3080         with self.assertRaises(DecodeError):
3081             ObjectIdentifier().decode(hexdec("060a2a80864886f70d01010b"))
3082
3083     def test_x690_vector(self):
3084         self.assertEqual(
3085             ObjectIdentifier().decode(hexdec("0603883703"))[0],
3086             ObjectIdentifier((2, 999, 3)),
3087         )
3088
3089     def test_nonnormalized_first_arc(self):
3090         tampered = (
3091             ObjectIdentifier.tag_default +
3092             len_encode(2) +
3093             b'\x80' +
3094             ObjectIdentifier((1, 0)).encode()[-1:]
3095         )
3096         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
3097         self.assertTrue(obj.ber_encoded)
3098         self.assertTrue(obj.bered)
3099         obj = copy(obj)
3100         self.assertTrue(obj.ber_encoded)
3101         self.assertTrue(obj.bered)
3102         with self.assertRaisesRegex(DecodeError, "non normalized arc encoding"):
3103             ObjectIdentifier().decode(tampered)
3104
3105     @given(data_strategy())
3106     def test_negative_arcs(self, d):
3107         oid = list(d.draw(oid_strategy()))
3108         if len(oid) == 2:
3109             return
3110         idx = d.draw(integers(min_value=3, max_value=len(oid)))
3111         oid[idx - 1] *= -1
3112         if oid[idx - 1] == 0:
3113             oid[idx - 1] = -1
3114         with self.assertRaises(InvalidOID):
3115             ObjectIdentifier(tuple(oid))
3116         with self.assertRaises(InvalidOID):
3117             ObjectIdentifier(".".join(str(i) for i in oid))
3118
3119     @given(data_strategy())
3120     def test_plused_arcs(self, d):
3121         oid = [str(arc) for arc in d.draw(oid_strategy())]
3122         idx = d.draw(integers(min_value=0, max_value=len(oid)))
3123         oid[idx - 1] = "+" + oid[idx - 1]
3124         with self.assertRaises(InvalidOID):
3125             ObjectIdentifier(".".join(str(i) for i in oid))
3126
3127     @given(data_strategy())
3128     def test_nonnormalized_arcs(self, d):
3129         arcs = d.draw(lists(
3130             integers(min_value=0, max_value=100),
3131             min_size=1,
3132             max_size=5,
3133         ))
3134         dered = ObjectIdentifier((1, 0) + tuple(arcs)).encode()
3135         _, _, lv = tag_strip(dered)
3136         _, _, v = len_decode(lv)
3137         v_no_first_arc = v[1:]
3138         idx_for_tamper = d.draw(integers(
3139             min_value=0,
3140             max_value=len(v_no_first_arc) - 1,
3141         ))
3142         tampered = list(bytearray(v_no_first_arc))
3143         for _ in range(d.draw(integers(min_value=1, max_value=3))):
3144             tampered.insert(idx_for_tamper, 0x80)
3145         tampered = bytes(bytearray(tampered))
3146         tampered = (
3147             ObjectIdentifier.tag_default +
3148             len_encode(len(tampered)) +
3149             tampered
3150         )
3151         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
3152         self.assertTrue(obj.ber_encoded)
3153         self.assertTrue(obj.bered)
3154         obj = copy(obj)
3155         self.assertTrue(obj.ber_encoded)
3156         self.assertTrue(obj.bered)
3157         with self.assertRaisesRegex(DecodeError, "non normalized arc encoding"):
3158             ObjectIdentifier().decode(tampered)
3159
3160
3161 @composite
3162 def enumerated_values_strategy(draw, schema=None, do_expl=False):
3163     if schema is None:
3164         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
3165         values = list(draw(sets(
3166             integers(),
3167             min_size=len(schema),
3168             max_size=len(schema),
3169         )))
3170         schema = list(zip(schema, values))
3171     value = draw(one_of(none(), sampled_from([k for k, v in schema])))
3172     impl = None
3173     expl = None
3174     if do_expl:
3175         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3176     else:
3177         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3178     default = draw(one_of(none(), sampled_from([v for k, v in schema])))
3179     optional = draw(one_of(none(), booleans()))
3180     _decoded = (
3181         draw(integers(min_value=0)),
3182         draw(integers(min_value=0)),
3183         draw(integers(min_value=0)),
3184     )
3185     return (schema, value, impl, expl, default, optional, _decoded)
3186
3187
3188 class TestEnumerated(CommonMixin, TestCase):
3189     class EWhatever(Enumerated):
3190         schema = (("whatever", 0),)
3191
3192     base_klass = EWhatever
3193
3194     def test_schema_required(self):
3195         with self.assertRaisesRegex(ValueError, "schema must be specified"):
3196             Enumerated()
3197
3198     def test_invalid_value_type(self):
3199         with self.assertRaises(InvalidValueType) as err:
3200             self.base_klass((1, 2))
3201         repr(err.exception)
3202
3203     @given(sets(text_letters(), min_size=2))
3204     def test_unknown_name(self, schema_input):
3205         missing = schema_input.pop()
3206
3207         class E(Enumerated):
3208             schema = [(n, 123) for n in schema_input]
3209         with self.assertRaises(ObjUnknown) as err:
3210             E(missing)
3211         repr(err.exception)
3212
3213     @given(
3214         sets(text_letters(), min_size=2),
3215         sets(integers(), min_size=2),
3216     )
3217     def test_unknown_value(self, schema_input, values_input):
3218         schema_input.pop()
3219         missing_value = values_input.pop()
3220         _input = list(zip(schema_input, values_input))
3221
3222         class E(Enumerated):
3223             schema = _input
3224         with self.assertRaises(DecodeError) as err:
3225             E(missing_value)
3226         repr(err.exception)
3227
3228     @given(booleans())
3229     def test_optional(self, optional):
3230         obj = self.base_klass(default="whatever", optional=optional)
3231         self.assertTrue(obj.optional)
3232
3233     def test_ready(self):
3234         obj = self.base_klass()
3235         self.assertFalse(obj.ready)
3236         repr(obj)
3237         list(obj.pps())
3238         pprint(obj, big_blobs=True, with_decode_path=True)
3239         with self.assertRaises(ObjNotReady) as err:
3240             obj.encode()
3241         repr(err.exception)
3242         obj = self.base_klass("whatever")
3243         self.assertTrue(obj.ready)
3244         repr(obj)
3245         list(obj.pps())
3246         pprint(obj, big_blobs=True, with_decode_path=True)
3247
3248     @given(integers(), integers(), binary(min_size=1), binary(min_size=1))
3249     def test_comparison(self, value1, value2, tag1, tag2):
3250         class E(Enumerated):
3251             schema = (
3252                 ("whatever0", value1),
3253                 ("whatever1", value2),
3254             )
3255
3256         class EInherited(E):
3257             pass
3258         for klass in (E, EInherited):
3259             obj1 = klass(value1)
3260             obj2 = klass(value2)
3261             self.assertEqual(obj1 == obj2, value1 == value2)
3262             self.assertEqual(obj1 != obj2, value1 != value2)
3263             self.assertEqual(obj1 == int(obj2), value1 == value2)
3264             obj1 = klass(value1, impl=tag1)
3265             obj2 = klass(value1, impl=tag2)
3266             self.assertEqual(obj1 == obj2, tag1 == tag2)
3267             self.assertEqual(obj1 != obj2, tag1 != tag2)
3268
3269     @given(data_strategy())
3270     def test_call(self, d):
3271         (
3272             schema_initial,
3273             value_initial,
3274             impl_initial,
3275             expl_initial,
3276             default_initial,
3277             optional_initial,
3278             _decoded_initial,
3279         ) = d.draw(enumerated_values_strategy())
3280
3281         class E(Enumerated):
3282             schema = schema_initial
3283         obj_initial = E(
3284             value=value_initial,
3285             impl=impl_initial,
3286             expl=expl_initial,
3287             default=default_initial,
3288             optional=optional_initial or False,
3289             _decoded=_decoded_initial,
3290         )
3291         (
3292             _,
3293             value,
3294             impl,
3295             expl,
3296             default,
3297             optional,
3298             _decoded,
3299         ) = d.draw(enumerated_values_strategy(
3300             schema=schema_initial,
3301             do_expl=impl_initial is None,
3302         ))
3303         obj = obj_initial(
3304             value=value,
3305             impl=impl,
3306             expl=expl,
3307             default=default,
3308             optional=optional,
3309         )
3310         if obj.ready:
3311             value_expected = default if value is None else value
3312             value_expected = (
3313                 default_initial if value_expected is None
3314                 else value_expected
3315             )
3316             self.assertEqual(
3317                 int(obj),
3318                 dict(schema_initial).get(value_expected, value_expected),
3319             )
3320         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3321         self.assertEqual(obj.expl_tag, expl or expl_initial)
3322         self.assertEqual(
3323             obj.default,
3324             default_initial if default is None else default,
3325         )
3326         if obj.default is None:
3327             optional = optional_initial if optional is None else optional
3328             optional = False if optional is None else optional
3329         else:
3330             optional = True
3331         self.assertEqual(obj.optional, optional)
3332         self.assertEqual(obj.specs, dict(schema_initial))
3333
3334     @given(enumerated_values_strategy())
3335     def test_copy(self, values):
3336         schema_input, value, impl, expl, default, optional, _decoded = values
3337
3338         class E(Enumerated):
3339             schema = schema_input
3340         register_class(E)
3341         obj = E(
3342             value=value,
3343             impl=impl,
3344             expl=expl,
3345             default=default,
3346             optional=optional,
3347             _decoded=_decoded,
3348         )
3349         for copy_func in copy_funcs:
3350             obj_copied = copy_func(obj)
3351             self.assert_copied_basic_fields(obj, obj_copied)
3352             self.assertEqual(obj.specs, obj_copied.specs)
3353
3354     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3355     @given(data_strategy())
3356     def test_symmetric(self, d):
3357         schema_input, _, _, _, default, optional, _decoded = d.draw(
3358             enumerated_values_strategy(),
3359         )
3360         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
3361         offset = d.draw(integers(min_value=0))
3362         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
3363         tail_junk = d.draw(binary(max_size=5))
3364         decode_path = d.draw(decode_path_strat)
3365
3366         class E(Enumerated):
3367             schema = schema_input
3368         obj = E(
3369             value=value,
3370             default=default,
3371             optional=optional,
3372             _decoded=_decoded,
3373         )
3374         repr(obj)
3375         list(obj.pps())
3376         pprint(obj, big_blobs=True, with_decode_path=True)
3377         self.assertFalse(obj.expled)
3378         obj_encoded = obj.encode()
3379         self.assertEqual(encode2pass(obj), obj_encoded)
3380         obj_expled = obj(value, expl=tag_expl)
3381         self.assertTrue(obj_expled.expled)
3382         repr(obj_expled)
3383         list(obj_expled.pps())
3384         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3385         obj_expled_encoded = obj_expled.encode()
3386         ctx_copied = deepcopy(ctx_dummy)
3387         obj_decoded, tail = obj_expled.decode(
3388             obj_expled_encoded + tail_junk,
3389             offset=offset,
3390             ctx=ctx_copied,
3391         )
3392         self.assertDictEqual(ctx_copied, ctx_dummy)
3393         repr(obj_decoded)
3394         list(obj_decoded.pps())
3395         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3396         self.assertEqual(tail, tail_junk)
3397         self.assertEqual(obj_decoded, obj_expled)
3398         self.assertNotEqual(obj_decoded, obj)
3399         self.assertEqual(int(obj_decoded), int(obj_expled))
3400         self.assertEqual(int(obj_decoded), int(obj))
3401         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3402         self.assertEqual(obj_decoded.expl_tag, tag_expl)
3403         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3404         self.assertEqual(
3405             obj_decoded.expl_llen,
3406             len(len_encode(len(obj_encoded))),
3407         )
3408         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3409         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3410         self.assertEqual(
3411             obj_decoded.offset,
3412             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3413         )
3414         self.assertEqual(obj_decoded.expl_offset, offset)
3415         assert_exceeding_data(
3416             self,
3417             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3418             tail_junk,
3419         )
3420
3421         evgens = list(obj_expled.decode_evgen(
3422             obj_expled_encoded + tail_junk,
3423             offset=offset,
3424             decode_path=decode_path,
3425             ctx=ctx_copied,
3426         ))
3427         self.assertEqual(len(evgens), 1)
3428         _decode_path, obj, tail = evgens[0]
3429         self.assertSequenceEqual(tail, tail_junk)
3430         self.assertEqual(_decode_path, decode_path)
3431         self.assertEqual(obj, obj_decoded)
3432         self.assertEqual(obj.expl_offset, offset)
3433         repr(obj)
3434         list(obj.pps())
3435
3436
3437 @composite
3438 def string_values_strategy(draw, alphabet, do_expl=False):
3439     bound_min, bound_max = sorted(draw(sets(
3440         integers(min_value=0, max_value=1 << 7),
3441         min_size=2,
3442         max_size=2,
3443     )))
3444     value = draw(one_of(
3445         none(),
3446         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3447     ))
3448     default = draw(one_of(
3449         none(),
3450         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3451     ))
3452     bounds = None
3453     if draw(booleans()):
3454         bounds = (bound_min, bound_max)
3455     impl = None
3456     expl = None
3457     if do_expl:
3458         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3459     else:
3460         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3461     optional = draw(one_of(none(), booleans()))
3462     _decoded = (
3463         draw(integers(min_value=0)),
3464         draw(integers(min_value=0)),
3465         draw(integers(min_value=0)),
3466     )
3467     return (value, bounds, impl, expl, default, optional, _decoded)
3468
3469
3470 class StringMixin(object):
3471     def test_invalid_value_type(self):
3472         with self.assertRaises(InvalidValueType) as err:
3473             self.base_klass((1, 2))
3474         repr(err.exception)
3475
3476     def text_alphabet(self):
3477         return "".join(chr(c) for c in range(256))
3478
3479     @given(booleans())
3480     def test_optional(self, optional):
3481         obj = self.base_klass(default=self.base_klass(""), optional=optional)
3482         self.assertTrue(obj.optional)
3483
3484     @given(data_strategy())
3485     def test_ready(self, d):
3486         obj = self.base_klass()
3487         self.assertFalse(obj.ready)
3488         repr(obj)
3489         list(obj.pps())
3490         pprint(obj, big_blobs=True, with_decode_path=True)
3491         str(obj)
3492         with self.assertRaises(ObjNotReady) as err:
3493             obj.encode()
3494         repr(err.exception)
3495         with self.assertRaises(ObjNotReady) as err:
3496             encode2pass(obj)
3497         value = d.draw(text(alphabet=self.text_alphabet()))
3498         obj = self.base_klass(value)
3499         self.assertTrue(obj.ready)
3500         repr(obj)
3501         list(obj.pps())
3502         pprint(obj, big_blobs=True, with_decode_path=True)
3503         str(obj)
3504
3505     @given(data_strategy())
3506     def test_comparison(self, d):
3507         value1 = d.draw(text(alphabet=self.text_alphabet()))
3508         value2 = d.draw(text(alphabet=self.text_alphabet()))
3509         tag1 = d.draw(binary(min_size=1))
3510         tag2 = d.draw(binary(min_size=1))
3511         obj1 = self.base_klass(value1)
3512         obj2 = self.base_klass(value2)
3513         self.assertEqual(obj1 == obj2, value1 == value2)
3514         self.assertEqual(obj1 != obj2, value1 != value2)
3515         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3516         self.assertEqual(obj1 == str(obj2), value1 == value2)
3517         obj1 = self.base_klass(value1, impl=tag1)
3518         obj2 = self.base_klass(value1, impl=tag2)
3519         self.assertEqual(obj1 == obj2, tag1 == tag2)
3520         self.assertEqual(obj1 != obj2, tag1 != tag2)
3521
3522     @given(data_strategy())
3523     def test_bounds_satisfied(self, d):
3524         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
3525         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3526         value = d.draw(text(
3527             alphabet=self.text_alphabet(),
3528             min_size=bound_min,
3529             max_size=bound_max,
3530         ))
3531         self.base_klass(value=value, bounds=(bound_min, bound_max))
3532
3533     @given(data_strategy())
3534     def test_bounds_unsatisfied(self, d):
3535         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
3536         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3537         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
3538         with self.assertRaises(BoundsError) as err:
3539             self.base_klass(value=value, bounds=(bound_min, bound_max))
3540         repr(err.exception)
3541         with self.assertRaisesRegex(DecodeError, "bounds") as err:
3542             self.base_klass(bounds=(bound_min, bound_max)).decode(
3543                 self.base_klass(value).encode()
3544             )
3545         repr(err.exception)
3546         with self.assertRaisesRegex(DecodeError, "bounds") as err:
3547             self.base_klass(bounds=(bound_min, bound_max)).decode(
3548                 encode2pass(self.base_klass(value))
3549             )
3550         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
3551         with self.assertRaises(BoundsError) as err:
3552             self.base_klass(value=value, bounds=(bound_min, bound_max))
3553         repr(err.exception)
3554         with self.assertRaisesRegex(DecodeError, "bounds") as err:
3555             self.base_klass(bounds=(bound_min, bound_max)).decode(
3556                 self.base_klass(value).encode()
3557             )
3558         repr(err.exception)
3559         with self.assertRaisesRegex(DecodeError, "bounds") as err:
3560             self.base_klass(bounds=(bound_min, bound_max)).decode(
3561                 encode2pass(self.base_klass(value))
3562             )
3563
3564     @given(data_strategy())
3565     def test_call(self, d):
3566         (
3567             value_initial,
3568             bounds_initial,
3569             impl_initial,
3570             expl_initial,
3571             default_initial,
3572             optional_initial,
3573             _decoded_initial,
3574         ) = d.draw(string_values_strategy(self.text_alphabet()))
3575         obj_initial = self.base_klass(
3576             value_initial,
3577             bounds_initial,
3578             impl_initial,
3579             expl_initial,
3580             default_initial,
3581             optional_initial or False,
3582             _decoded_initial,
3583         )
3584         (
3585             value,
3586             bounds,
3587             impl,
3588             expl,
3589             default,
3590             optional,
3591             _decoded,
3592         ) = d.draw(string_values_strategy(
3593             self.text_alphabet(),
3594             do_expl=impl_initial is None,
3595         ))
3596         if (default is None) and (obj_initial.default is not None):
3597             bounds = None
3598         if (
3599                 (bounds is None) and
3600                 (value is not None) and
3601                 (bounds_initial is not None) and
3602                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
3603         ):
3604             value = None
3605         if (
3606                 (bounds is None) and
3607                 (default is not None) and
3608                 (bounds_initial is not None) and
3609                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
3610         ):
3611             default = None
3612         obj = obj_initial(value, bounds, impl, expl, default, optional)
3613         if obj.ready:
3614             value_expected = default if value is None else value
3615             value_expected = (
3616                 default_initial if value_expected is None
3617                 else value_expected
3618             )
3619             self.assertEqual(obj, value_expected)
3620         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3621         self.assertEqual(obj.expl_tag, expl or expl_initial)
3622         self.assertEqual(
3623             obj.default,
3624             default_initial if default is None else default,
3625         )
3626         if obj.default is None:
3627             optional = optional_initial if optional is None else optional
3628             optional = False if optional is None else optional
3629         else:
3630             optional = True
3631         self.assertEqual(obj.optional, optional)
3632         self.assertEqual(
3633             (obj._bound_min, obj._bound_max),
3634             bounds or bounds_initial or (0, float("+inf")),
3635         )
3636
3637     @given(data_strategy())
3638     def test_copy(self, d):
3639         values = d.draw(string_values_strategy(self.text_alphabet()))
3640         obj = self.base_klass(*values)
3641         for copy_func in copy_funcs:
3642             obj_copied = copy_func(obj)
3643             self.assert_copied_basic_fields(obj, obj_copied)
3644             self.assertEqual(obj._bound_min, obj_copied._bound_min)
3645             self.assertEqual(obj._bound_max, obj_copied._bound_max)
3646             self.assertEqual(obj._value, obj_copied._value)
3647
3648     @given(data_strategy())
3649     def test_stripped(self, d):
3650         value = d.draw(text(alphabet=self.text_alphabet()))
3651         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3652         obj = self.base_klass(value, impl=tag_impl)
3653         with self.assertRaises(NotEnoughData):
3654             obj.decode(obj.encode()[:-1])
3655
3656     @given(data_strategy())
3657     def test_stripped_expl(self, d):
3658         value = d.draw(text(alphabet=self.text_alphabet()))
3659         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3660         obj = self.base_klass(value, expl=tag_expl)
3661         with self.assertRaises(NotEnoughData):
3662             obj.decode(obj.encode()[:-1])
3663
3664     @given(
3665         integers(min_value=31),
3666         integers(min_value=0),
3667         decode_path_strat,
3668     )
3669     def test_bad_tag(self, tag, offset, decode_path):
3670         with self.assertRaises(DecodeError) as err:
3671             self.base_klass().decode(
3672                 tag_encode(tag)[:-1],
3673                 offset=offset,
3674                 decode_path=decode_path,
3675             )
3676         repr(err.exception)
3677         self.assertEqual(err.exception.offset, offset)
3678         self.assertEqual(err.exception.decode_path, decode_path)
3679
3680     @given(
3681         integers(min_value=128),
3682         integers(min_value=0),
3683         decode_path_strat,
3684     )
3685     def test_bad_len(self, l, offset, decode_path):
3686         with self.assertRaises(DecodeError) as err:
3687             self.base_klass().decode(
3688                 self.base_klass.tag_default + len_encode(l)[:-1],
3689                 offset=offset,
3690                 decode_path=decode_path,
3691             )
3692         repr(err.exception)
3693         self.assertEqual(err.exception.offset, offset)
3694         self.assertEqual(err.exception.decode_path, decode_path)
3695
3696     @given(
3697         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3698         integers(min_value=0),
3699         decode_path_strat,
3700     )
3701     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3702         value, bound_min = list(sorted(ints))
3703
3704         class String(self.base_klass):
3705             # Multiply this value by four, to satisfy UTF-32 bounds
3706             # (4 bytes per character) validation
3707             bounds = (bound_min * 4, bound_min * 4)
3708         with self.assertRaises(DecodeError) as err:
3709             String().decode(
3710                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
3711                 offset=offset,
3712                 decode_path=decode_path,
3713             )
3714         repr(err.exception)
3715         self.assertEqual(err.exception.offset, offset)
3716         self.assertEqual(err.exception.decode_path, decode_path)
3717
3718     @given(data_strategy())
3719     def test_symmetric(self, d):
3720         values = d.draw(string_values_strategy(self.text_alphabet()))
3721         value = d.draw(text(alphabet=self.text_alphabet()))
3722         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3723         offset = d.draw(integers(min_value=0))
3724         tail_junk = d.draw(binary(max_size=5))
3725         decode_path = d.draw(decode_path_strat)
3726         _, _, _, _, default, optional, _decoded = values
3727         obj = self.base_klass(
3728             value=value,
3729             default=default,
3730             optional=optional,
3731             _decoded=_decoded,
3732         )
3733         repr(obj)
3734         list(obj.pps())
3735         pprint(obj, big_blobs=True, with_decode_path=True)
3736         self.assertFalse(obj.expled)
3737         obj_encoded = obj.encode()
3738         self.assertEqual(encode2pass(obj), obj_encoded)
3739         obj_expled = obj(value, expl=tag_expl)
3740         self.assertTrue(obj_expled.expled)
3741         repr(obj_expled)
3742         list(obj_expled.pps())
3743         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3744         obj_expled_encoded = obj_expled.encode()
3745         ctx_copied = deepcopy(ctx_dummy)
3746         obj_decoded, tail = obj_expled.decode(
3747             obj_expled_encoded + tail_junk,
3748             offset=offset,
3749             ctx=ctx_copied,
3750         )
3751         self.assertDictEqual(ctx_copied, ctx_dummy)
3752         repr(obj_decoded)
3753         list(obj_decoded.pps())
3754         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3755         self.assertEqual(tail, tail_junk)
3756         self.assertEqual(obj_decoded, obj_expled)
3757         self.assertNotEqual(obj_decoded, obj)
3758         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3759         self.assertEqual(bytes(obj_decoded), bytes(obj))
3760         self.assertEqual(str(obj_decoded), str(obj_expled))
3761         self.assertEqual(str(obj_decoded), str(obj))
3762         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3763         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3764         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3765         self.assertEqual(
3766             obj_decoded.expl_llen,
3767             len(len_encode(len(obj_encoded))),
3768         )
3769         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3770         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3771         self.assertEqual(
3772             obj_decoded.offset,
3773             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3774         )
3775         self.assertEqual(obj_decoded.expl_offset, offset)
3776         assert_exceeding_data(
3777             self,
3778             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3779             tail_junk,
3780         )
3781
3782         evgens = list(obj_expled.decode_evgen(
3783             obj_expled_encoded + tail_junk,
3784             offset=offset,
3785             decode_path=decode_path,
3786             ctx=ctx_copied,
3787         ))
3788         self.assertEqual(len(evgens), 1)
3789         _decode_path, obj, tail = evgens[0]
3790         self.assertSequenceEqual(tail, tail_junk)
3791         self.assertEqual(_decode_path, decode_path)
3792         if not getattr(self, "evgen_mode_skip_value", True):
3793             self.assertEqual(obj, obj_decoded)
3794         self.assertEqual(obj.expl_offset, offset)
3795         repr(obj)
3796         list(obj.pps())
3797
3798
3799 cyrillic_letters = text(
3800     alphabet="".join(chr(i) for i in list(range(0x0410, 0x044f + 1))),
3801     min_size=1,
3802     max_size=5,
3803 )
3804
3805
3806 class TestUTF8String(StringMixin, CommonMixin, TestCase):
3807     base_klass = UTF8String
3808
3809     @given(cyrillic_letters)
3810     def test_byte_per_primitive(self, chars):
3811         char = chars[0]
3812         char_raw = char.encode("utf-8")
3813         encoded = b"".join((
3814             self.base_klass().tag_constructed,
3815             LENINDEF,
3816             OctetString(char_raw[:1]).encode(),
3817             OctetString(char_raw[1:2]).encode(),
3818             EOC,
3819         ))
3820         self.assertEqual(
3821             self.base_klass().decod(encoded, ctx={"bered": True}),
3822             char,
3823         )
3824
3825
3826 class UnicodeDecodeErrorMixin(object):
3827     @given(cyrillic_letters)
3828     def test_unicode_decode_error(self, cyrillic_text):
3829         with self.assertRaises(DecodeError):
3830             self.base_klass(cyrillic_text)
3831
3832
3833 class TestNumericString(StringMixin, CommonMixin, TestCase):
3834     base_klass = NumericString
3835
3836     def text_alphabet(self):
3837         return digits + " "
3838
3839     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
3840     def test_non_numeric(self, non_numeric_text):
3841         with self.assertRaisesRegex(DecodeError, "alphabet value"):
3842             self.base_klass(non_numeric_text)
3843
3844     @given(
3845         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3846         integers(min_value=0),
3847         decode_path_strat,
3848     )
3849     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3850         value, bound_min = list(sorted(ints))
3851
3852         class String(self.base_klass):
3853             bounds = (bound_min, bound_min)
3854         with self.assertRaises(DecodeError) as err:
3855             String().decode(
3856                 self.base_klass(b"1" * value).encode(),
3857                 offset=offset,
3858                 decode_path=decode_path,
3859             )
3860         repr(err.exception)
3861         self.assertEqual(err.exception.offset, offset)
3862         self.assertEqual(err.exception.decode_path, decode_path)
3863
3864     def test_byte_per_primitive(self):
3865         encoded = b"".join((
3866             self.base_klass().tag_constructed,
3867             LENINDEF,
3868             OctetString(b"1").encode(),
3869             OctetString(b"2").encode(),
3870             EOC,
3871         ))
3872         self.assertEqual(
3873             self.base_klass().decod(encoded, ctx={"bered": True}),
3874             "12",
3875         )
3876
3877
3878 class TestPrintableString(
3879         UnicodeDecodeErrorMixin,
3880         StringMixin,
3881         CommonMixin,
3882         TestCase,
3883 ):
3884     base_klass = PrintableString
3885
3886     def text_alphabet(self):
3887         return ascii_letters + digits + " '()+,-./:=?"
3888
3889     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
3890     def test_non_printable(self, non_printable_text):
3891         with self.assertRaisesRegex(DecodeError, "alphabet value"):
3892             self.base_klass(non_printable_text)
3893
3894     @given(
3895         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3896         integers(min_value=0),
3897         decode_path_strat,
3898     )
3899     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3900         value, bound_min = list(sorted(ints))
3901
3902         class String(self.base_klass):
3903             bounds = (bound_min, bound_min)
3904         with self.assertRaises(DecodeError) as err:
3905             String().decode(
3906                 self.base_klass(b"1" * value).encode(),
3907                 offset=offset,
3908                 decode_path=decode_path,
3909             )
3910         repr(err.exception)
3911         self.assertEqual(err.exception.offset, offset)
3912         self.assertEqual(err.exception.decode_path, decode_path)
3913
3914     def test_allowable_invalid_chars(self):
3915         for c, kwargs in (
3916                 ("*", {"allow_asterisk": True}),
3917                 ("&", {"allow_ampersand": True}),
3918                 ("&*", {"allow_asterisk": True, "allow_ampersand": True}),
3919         ):
3920             s = "hello invalid"
3921             obj = self.base_klass(s)
3922             for prop in kwargs.keys():
3923                 self.assertFalse(getattr(obj, prop))
3924             s += c
3925             with self.assertRaisesRegex(DecodeError, "alphabet value"):
3926                 self.base_klass(s)
3927             self.base_klass(s, **kwargs)
3928             klass = self.base_klass(**kwargs)
3929             obj = klass(s)
3930             for prop in kwargs.keys():
3931                 self.assertTrue(getattr(obj, prop))
3932             obj = copy(obj)
3933             obj(s)
3934             for prop in kwargs.keys():
3935                 self.assertTrue(getattr(obj, prop))
3936
3937
3938 class TestTeletexString(
3939         UnicodeDecodeErrorMixin,
3940         StringMixin,
3941         CommonMixin,
3942         TestCase,
3943 ):
3944     base_klass = TeletexString
3945
3946
3947 class TestVideotexString(
3948         UnicodeDecodeErrorMixin,
3949         StringMixin,
3950         CommonMixin,
3951         TestCase,
3952 ):
3953     base_klass = VideotexString
3954
3955
3956 class TestIA5String(
3957         UnicodeDecodeErrorMixin,
3958         StringMixin,
3959         CommonMixin,
3960         TestCase,
3961 ):
3962     base_klass = IA5String
3963
3964     def text_alphabet(self):
3965         return "".join(chr(c) for c in range(128))
3966
3967     @given(integers(min_value=128, max_value=255))
3968     def test_alphabet_bad(self, code):
3969         with self.assertRaises(DecodeError):
3970             self.base_klass().decod(
3971                 self.base_klass.tag_default +
3972                 len_encode(1) +
3973                 bytes(bytearray([code])),
3974             )
3975
3976
3977 class TestGraphicString(
3978         UnicodeDecodeErrorMixin,
3979         StringMixin,
3980         CommonMixin,
3981         TestCase,
3982 ):
3983     base_klass = GraphicString
3984
3985
3986 class TestVisibleString(
3987         UnicodeDecodeErrorMixin,
3988         StringMixin,
3989         CommonMixin,
3990         TestCase,
3991 ):
3992     base_klass = VisibleString
3993
3994     def text_alphabet(self):
3995         return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
3996
3997     def test_x690_vector(self):
3998         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3999         self.assertSequenceEqual(tail, b"")
4000         self.assertEqual(str(obj), "Jones")
4001         self.assertFalse(obj.ber_encoded)
4002         self.assertFalse(obj.lenindef)
4003         self.assertFalse(obj.bered)
4004
4005         obj, tail = VisibleString().decode(
4006             hexdec("3A0904034A6F6E04026573"),
4007             ctx={"bered": True},
4008         )
4009         self.assertSequenceEqual(tail, b"")
4010         self.assertEqual(str(obj), "Jones")
4011         self.assertTrue(obj.ber_encoded)
4012         self.assertFalse(obj.lenindef)
4013         self.assertTrue(obj.bered)
4014         obj = copy(obj)
4015         self.assertTrue(obj.ber_encoded)
4016         self.assertFalse(obj.lenindef)
4017         self.assertTrue(obj.bered)
4018
4019         obj, tail = VisibleString().decode(
4020             hexdec("3A8004034A6F6E040265730000"),
4021             ctx={"bered": True},
4022         )
4023         self.assertSequenceEqual(tail, b"")
4024         self.assertEqual(str(obj), "Jones")
4025         self.assertTrue(obj.ber_encoded)
4026         self.assertTrue(obj.lenindef)
4027         self.assertTrue(obj.bered)
4028         obj = copy(obj)
4029         self.assertTrue(obj.ber_encoded)
4030         self.assertTrue(obj.lenindef)
4031         self.assertTrue(obj.bered)
4032
4033     @given(one_of((
4034         integers(min_value=0, max_value=ord(" ") - 1),
4035         integers(min_value=ord("~") + 1, max_value=255),
4036     )))
4037     def test_alphabet_bad(self, code):
4038         with self.assertRaises(DecodeError):
4039             self.base_klass().decod(
4040                 self.base_klass.tag_default +
4041                 len_encode(1) +
4042                 bytes(bytearray([code])),
4043             )
4044
4045     @given(
4046         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
4047         integers(min_value=0),
4048         decode_path_strat,
4049     )
4050     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
4051         value, bound_min = list(sorted(ints))
4052
4053         class String(self.base_klass):
4054             bounds = (bound_min, bound_min)
4055         with self.assertRaises(DecodeError) as err:
4056             String().decode(
4057                 self.base_klass(b"1" * value).encode(),
4058                 offset=offset,
4059                 decode_path=decode_path,
4060             )
4061         repr(err.exception)
4062         self.assertEqual(err.exception.offset, offset)
4063         self.assertEqual(err.exception.decode_path, decode_path)
4064
4065
4066 class TestGeneralString(
4067         UnicodeDecodeErrorMixin,
4068         StringMixin,
4069         CommonMixin,
4070         TestCase,
4071 ):
4072     base_klass = GeneralString
4073
4074
4075 class TestUniversalString(StringMixin, CommonMixin, TestCase):
4076     base_klass = UniversalString
4077
4078
4079 class TestBMPString(StringMixin, CommonMixin, TestCase):
4080     base_klass = BMPString
4081
4082
4083 @composite
4084 def generalized_time_values_strategy(
4085         draw,
4086         min_datetime,
4087         max_datetime,
4088         omit_ms=False,
4089         do_expl=False,
4090 ):
4091     value = None
4092     if draw(booleans()):
4093         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
4094         if omit_ms:
4095             value = value.replace(microsecond=0)
4096     default = None
4097     if draw(booleans()):
4098         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
4099         if omit_ms:
4100             default = default.replace(microsecond=0)
4101     impl = None
4102     expl = None
4103     if do_expl:
4104         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4105     else:
4106         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4107     optional = draw(one_of(none(), booleans()))
4108     _decoded = (
4109         draw(integers(min_value=0)),
4110         draw(integers(min_value=0)),
4111         draw(integers(min_value=0)),
4112     )
4113     return (value, impl, expl, default, optional, _decoded)
4114
4115
4116 class TimeMixin(object):
4117     def test_invalid_value_type(self):
4118         with self.assertRaises(InvalidValueType) as err:
4119             self.base_klass(datetime.now().timetuple())
4120         repr(err.exception)
4121
4122     @given(data_strategy())
4123     def test_optional(self, d):
4124         default = d.draw(datetimes(
4125             min_value=self.min_datetime,
4126             max_value=self.max_datetime,
4127         ))
4128         optional = d.draw(booleans())
4129         obj = self.base_klass(default=default, optional=optional)
4130         self.assertTrue(obj.optional)
4131
4132     @given(data_strategy())
4133     def test_ready(self, d):
4134         obj = self.base_klass()
4135         self.assertFalse(obj.ready)
4136         repr(obj)
4137         list(obj.pps())
4138         pprint(obj, big_blobs=True, with_decode_path=True)
4139         with self.assertRaises(ObjNotReady) as err:
4140             obj.encode()
4141         repr(err.exception)
4142         with self.assertRaises(ObjNotReady) as err:
4143             encode2pass(obj)
4144         value = d.draw(datetimes(
4145             min_value=self.min_datetime,
4146             max_value=self.max_datetime,
4147         ))
4148         obj = self.base_klass(value)
4149         self.assertTrue(obj.ready)
4150         repr(obj)
4151         list(obj.pps())
4152         pprint(obj, big_blobs=True, with_decode_path=True)
4153
4154     @given(data_strategy())
4155     def test_comparison(self, d):
4156         value1 = d.draw(datetimes(
4157             min_value=self.min_datetime,
4158             max_value=self.max_datetime,
4159         ))
4160         value2 = d.draw(datetimes(
4161             min_value=self.min_datetime,
4162             max_value=self.max_datetime,
4163         ))
4164         tag1 = d.draw(binary(min_size=1))
4165         tag2 = d.draw(binary(min_size=1))
4166         if self.omit_ms:
4167             value1 = value1.replace(microsecond=0)
4168             value2 = value2.replace(microsecond=0)
4169         obj1 = self.base_klass(value1)
4170         obj2 = self.base_klass(value2)
4171         self.assertEqual(obj1 == obj2, value1 == value2)
4172         self.assertEqual(obj1 != obj2, value1 != value2)
4173         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
4174         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
4175         obj1 = self.base_klass(value1, impl=tag1)
4176         obj2 = self.base_klass(value1, impl=tag2)
4177         self.assertEqual(obj1 == obj2, tag1 == tag2)
4178         self.assertEqual(obj1 != obj2, tag1 != tag2)
4179
4180     @given(data_strategy())
4181     def test_call(self, d):
4182         (
4183             value_initial,
4184             impl_initial,
4185             expl_initial,
4186             default_initial,
4187             optional_initial,
4188             _decoded_initial,
4189         ) = d.draw(generalized_time_values_strategy(
4190             min_datetime=self.min_datetime,
4191             max_datetime=self.max_datetime,
4192             omit_ms=self.omit_ms,
4193         ))
4194         obj_initial = self.base_klass(
4195             value=value_initial,
4196             impl=impl_initial,
4197             expl=expl_initial,
4198             default=default_initial,
4199             optional=optional_initial or False,
4200             _decoded=_decoded_initial,
4201         )
4202         (
4203             value,
4204             impl,
4205             expl,
4206             default,
4207             optional,
4208             _decoded,
4209         ) = d.draw(generalized_time_values_strategy(
4210             min_datetime=self.min_datetime,
4211             max_datetime=self.max_datetime,
4212             omit_ms=self.omit_ms,
4213             do_expl=impl_initial is None,
4214         ))
4215         obj = obj_initial(
4216             value=value,
4217             impl=impl,
4218             expl=expl,
4219             default=default,
4220             optional=optional,
4221         )
4222         if obj.ready:
4223             value_expected = default if value is None else value
4224             value_expected = (
4225                 default_initial if value_expected is None
4226                 else value_expected
4227             )
4228             self.assertEqual(obj, value_expected)
4229         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4230         self.assertEqual(obj.expl_tag, expl or expl_initial)
4231         self.assertEqual(
4232             obj.default,
4233             default_initial if default is None else default,
4234         )
4235         if obj.default is None:
4236             optional = optional_initial if optional is None else optional
4237             optional = False if optional is None else optional
4238         else:
4239             optional = True
4240         self.assertEqual(obj.optional, optional)
4241
4242     @given(data_strategy())
4243     def test_copy(self, d):
4244         values = d.draw(generalized_time_values_strategy(
4245             min_datetime=self.min_datetime,
4246             max_datetime=self.max_datetime,
4247         ))
4248         obj = self.base_klass(*values)
4249         for copy_func in copy_funcs:
4250             obj_copied = copy_func(obj)
4251             self.assert_copied_basic_fields(obj, obj_copied)
4252             self.assertEqual(obj._value, obj_copied._value)
4253
4254     @given(data_strategy())
4255     def test_stripped(self, d):
4256         value = d.draw(datetimes(
4257             min_value=self.min_datetime,
4258             max_value=self.max_datetime,
4259         ))
4260         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4261         obj = self.base_klass(value, impl=tag_impl)
4262         with self.assertRaises(NotEnoughData):
4263             obj.decode(obj.encode()[:-1])
4264
4265     @given(data_strategy())
4266     def test_stripped_expl(self, d):
4267         value = d.draw(datetimes(
4268             min_value=self.min_datetime,
4269             max_value=self.max_datetime,
4270         ))
4271         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4272         obj = self.base_klass(value, expl=tag_expl)
4273         with self.assertRaises(NotEnoughData):
4274             obj.decode(obj.encode()[:-1])
4275
4276     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4277     @given(data_strategy())
4278     def test_symmetric(self, d):
4279         values = d.draw(generalized_time_values_strategy(
4280             min_datetime=self.min_datetime,
4281             max_datetime=self.max_datetime,
4282         ))
4283         value = d.draw(datetimes(
4284             min_value=self.min_datetime,
4285             max_value=self.max_datetime,
4286         ))
4287         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4288         offset = d.draw(integers(min_value=0))
4289         tail_junk = d.draw(binary(max_size=5))
4290         _, _, _, default, optional, _decoded = values
4291         obj = self.base_klass(
4292             value=value,
4293             default=default,
4294             optional=optional,
4295             _decoded=_decoded,
4296         )
4297         repr(obj)
4298         list(obj.pps())
4299         pprint(obj, big_blobs=True, with_decode_path=True)
4300         self.assertFalse(obj.expled)
4301         obj_encoded = obj.encode()
4302         self.assertEqual(encode2pass(obj), obj_encoded)
4303         self.additional_symmetric_check(value, obj_encoded)
4304         obj_expled = obj(value, expl=tag_expl)
4305         self.assertTrue(obj_expled.expled)
4306         repr(obj_expled)
4307         list(obj_expled.pps())
4308         pprint(obj_expled, big_blobs=True, with_decode_path=True)
4309         obj_expled_encoded = obj_expled.encode()
4310         ctx_copied = deepcopy(ctx_dummy)
4311         obj_decoded, tail = obj_expled.decode(
4312             obj_expled_encoded + tail_junk,
4313             offset=offset,
4314             ctx=ctx_copied,
4315         )
4316         self.assertDictEqual(ctx_copied, ctx_dummy)
4317         repr(obj_decoded)
4318         list(obj_decoded.pps())
4319         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4320         self.assertEqual(tail, tail_junk)
4321         self.assertEqual(obj_decoded, obj_expled)
4322         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
4323         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
4324         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4325         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4326         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4327         self.assertEqual(
4328             obj_decoded.expl_llen,
4329             len(len_encode(len(obj_encoded))),
4330         )
4331         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4332         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4333         self.assertEqual(
4334             obj_decoded.offset,
4335             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4336         )
4337         self.assertEqual(obj_decoded.expl_offset, offset)
4338         assert_exceeding_data(
4339             self,
4340             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
4341             tail_junk,
4342         )
4343
4344
4345 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
4346     base_klass = GeneralizedTime
4347     omit_ms = False
4348     min_datetime = datetime(1900, 1, 1)
4349     max_datetime = datetime(9999, 12, 31)
4350     evgen_mode_skip_value = False
4351
4352     def additional_symmetric_check(self, value, obj_encoded):
4353         if value.microsecond > 0:
4354             self.assertFalse(obj_encoded.endswith(b"0Z"))
4355
4356     def test_repr_not_ready(self):
4357         str(GeneralizedTime())
4358         repr(GeneralizedTime())
4359
4360     def test_x690_vector_valid(self):
4361         for data in ((
4362                 b"19920521000000Z",
4363                 b"19920622123421Z",
4364                 b"19920722132100.3Z",
4365         )):
4366             GeneralizedTime(data)
4367
4368     def test_x690_vector_invalid(self):
4369         for data in ((
4370                 b"19920520240000Z",
4371                 b"19920622123421.0Z",
4372                 b"19920722132100.30Z",
4373         )):
4374             with self.assertRaises(DecodeError) as err:
4375                 GeneralizedTime(data)
4376             repr(err.exception)
4377
4378     def test_go_vectors_invalid(self):
4379         for data in ((
4380                 b"20100102030405",
4381                 b"00000100000000Z",
4382                 b"20101302030405Z",
4383                 b"20100002030405Z",
4384                 b"20100100030405Z",
4385                 b"20100132030405Z",
4386                 b"20100231030405Z",
4387                 b"20100102240405Z",
4388                 b"20100102036005Z",
4389                 b"20100102030460Z",
4390                 b"-20100102030410Z",
4391                 b"2010-0102030410Z",
4392                 b"2010-0002030410Z",
4393                 b"201001-02030410Z",
4394                 b"20100102-030410Z",
4395                 b"2010010203-0410Z",
4396                 b"201001020304-10Z",
4397                 # These ones are INVALID in *DER*, but accepted
4398                 # by Go's encoding/asn1
4399                 b"20100102030405+0607",
4400                 b"20100102030405-0607",
4401         )):
4402             with self.assertRaises(DecodeError) as err:
4403                 GeneralizedTime(data)
4404             repr(err.exception)
4405
4406     def test_go_vectors_valid(self):
4407         self.assertEqual(
4408             GeneralizedTime(b"20100102030405Z").todatetime(),
4409             datetime(2010, 1, 2, 3, 4, 5, 0),
4410         )
4411
4412     def test_go_vectors_valid_ber(self):
4413         for data in ((
4414                 b"20100102030405+0607",
4415                 b"20100102030405-0607",
4416         )):
4417             GeneralizedTime(data, ctx={"bered": True})
4418
4419     def test_utc_offsets(self):
4420         """Some know equal UTC offsets
4421         """
4422         dts = [
4423             GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4424             for data in (
4425                 "200101011830Z",
4426                 "200101012230+04",
4427                 "200101011130-0700",
4428                 "200101011500-03:30",
4429             )
4430         ]
4431         self.assertEqual(dts[0], dts[1])
4432         self.assertEqual(dts[0], dts[2])
4433         self.assertEqual(dts[0], dts[3])
4434
4435     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4436     @given(data_strategy())
4437     def test_valid_ber(self, d):
4438         year = d.draw(integers(min_value=2, max_value=9999))
4439         month = d.draw(integers(min_value=1, max_value=12))
4440         day = d.draw(integers(min_value=1, max_value=28))
4441         hours = d.draw(integers(min_value=0, max_value=23))
4442         data = "%04d%02d%02d%02d" % (year, month, day, hours)
4443         dt = datetime(year, month, day, hours)
4444         fractions_sign = d.draw(sampled_from("  ,."))
4445         fractions = None
4446         if fractions_sign != " ":
4447             fractions = random()
4448         if d.draw(booleans()):
4449             minutes = d.draw(integers(min_value=0, max_value=59))
4450             data += "%02d" % minutes
4451             dt += timedelta(seconds=60 * minutes)
4452             if d.draw(booleans()):
4453                 seconds = d.draw(integers(min_value=0, max_value=59))
4454                 data += "%02d" % seconds
4455                 dt += timedelta(seconds=seconds)
4456                 if fractions is not None:
4457                     dt += timedelta(microseconds=10**6 * fractions)
4458             elif fractions is not None:
4459                 dt += timedelta(seconds=60 * fractions)
4460         elif fractions is not None:
4461             dt += timedelta(seconds=3600 * fractions)
4462         if fractions is not None:
4463             data += fractions_sign + str(fractions)[2:]
4464         if d.draw(booleans()):
4465             data += "Z"
4466         elif d.draw(booleans()):
4467             offset_hour = d.draw(integers(min_value=0, max_value=13))
4468             sign = 1
4469             if d.draw(booleans()):
4470                 data += "-"
4471                 sign = -1
4472             else:
4473                 data += "+"
4474             dt -= timedelta(seconds=sign * 3600 * offset_hour)
4475             data += "%02d" % offset_hour
4476             minutes_separator = d.draw(sampled_from((None, "", ":")))
4477             if minutes_separator is not None:
4478                 offset_minute = d.draw(integers(min_value=0, max_value=59))
4479                 dt -= timedelta(seconds=sign * 60 * offset_minute)
4480                 data += "%s%02d" % (minutes_separator, offset_minute)
4481         data = data.encode("ascii")
4482         data_der = GeneralizedTime.tag_default + len_encode(len(data)) + data
4483         try:
4484             GeneralizedTime().decod(data_der)
4485         except DecodeError:
4486             dered = False
4487         else:
4488             dered = True
4489         obj = GeneralizedTime().decod(data_der, ctx={"bered": True})
4490         if dt.year > 1970:
4491             self.assertEqual(
4492                 mktime(obj.todatetime().timetuple()),
4493                 mktime(dt.timetuple()),
4494             )
4495         else:
4496             try:
4497                 obj.todatetime().timestamp()
4498             except:
4499                 pass
4500             else:
4501                 self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
4502         self.assertEqual(obj.ber_encoded, not dered)
4503         self.assertEqual(obj.bered, not dered)
4504         self.assertEqual(obj.ber_raw, None if dered else data)
4505         self.assertEqual(obj.encode() == data_der, dered)
4506         repr(obj)
4507         bytes(obj)
4508         str(obj)
4509
4510     def test_invalid_ber(self):
4511         for data in ((
4512                 # "00010203040506.07",
4513                 "-0010203040506.07",
4514                 "0001-203040506.07",
4515                 "000102-3040506.07",
4516                 "00010203-40506.07",
4517                 "0001020304-506.07",
4518                 "000102030405-6.07",
4519                 "00010203040506.-7",
4520                 "+0010203040506.07",
4521                 "0001+203040506.07",
4522                 "000102+3040506.07",
4523                 "00010203+40506.07",
4524                 "0001020304+506.07",
4525                 "000102030405+6.07",
4526                 "00010203040506.+7",
4527                 " 0010203040506.07",
4528                 "0001 203040506.07",
4529                 "000102 3040506.07",
4530                 "00010203 40506.07",
4531                 "0001020304 506.07",
4532                 "000102030405 6.07",
4533                 "00010203040506. 7",
4534                 "001 0203040506.07",
4535                 "00012 03040506.07",
4536                 "0001023 040506.07",
4537                 "000102034 0506.07",
4538                 "00010203045 06.07",
4539                 "0001020304056 .07",
4540                 "00010203040506.7 ",
4541                 "00010203040506.",
4542                 "0001020304050607",
4543
4544                 "-0010203040506",
4545                 "0001-203040506",
4546                 "000102-3040506",
4547                 "00010203-40506",
4548                 "0001020304-506",
4549                 "000102030405-6",
4550                 "0001+203040506",
4551                 "000102+3040506",
4552                 "00010203+40506",
4553                 "0001020304+506",
4554                 "000102030405+6",
4555                 " 0010203040506",
4556                 "0001 203040506",
4557                 "000102 3040506",
4558                 "00010203 40506",
4559                 "0001020304 506",
4560                 "000102030405 6",
4561                 "001 0203040506",
4562                 "00012 03040506",
4563                 "0001023 040506",
4564                 "000102034 0506",
4565                 "00010203045 06",
4566                 "0001020304056 ",
4567
4568                 "-00102030405.07",
4569                 "0001-2030405.07",
4570                 "000102-30405.07",
4571                 "00010203-405.07",
4572                 "0001020304-5.07",
4573                 "000102030405.-7",
4574                 "+00102030405.07",
4575                 "0001+2030405.07",
4576                 "00010203+405.07",
4577                 "0001020304+5.07",
4578                 "000102030405.+7",
4579                 " 00102030405.07",
4580                 "0001 2030405.07",
4581                 "000102 30405.07",
4582                 "00010203 405.07",
4583                 "0001020304 5.07",
4584                 "000102030405. 7",
4585                 "001 02030405.07",
4586                 "00012 030405.07",
4587                 "0001023 0405.07",
4588                 "000102034 05.07",
4589                 "00010203045 .07",
4590                 "000102030405.7 ",
4591                 "000102030405.",
4592
4593                 "-001020304.07",
4594                 "0001-20304.07",
4595                 "000102-304.07",
4596                 "00010203-4.07",
4597                 "0001020304.-7",
4598                 "+001020304.07",
4599                 "0001+20304.07",
4600                 "00010203+4.07",
4601                 "0001020304.+7",
4602                 " 001020304.07",
4603                 "0001 20304.07",
4604                 "000102 304.07",
4605                 "00010203 4.07",
4606                 "0001020304. 7",
4607                 "001 020304.07",
4608                 "00012 0304.07",
4609                 "0001023 04.07",
4610                 "000102034 .07",
4611                 "0001020304.7 ",
4612                 "0001020304.",
4613
4614                 "00010203",
4615                 "00010203040506Y",
4616                 "0001010100+0001",
4617                 "0001010100+00:01",
4618                 "0001010100+01",
4619
4620                 "00010203040506.07+15",
4621                 "00010203040506.07-15",
4622                 "00010203040506.07+14:60",
4623                 "00010203040506.07+1460",
4624                 "00010203040506.07-1460",
4625                 "00010203040506.07+00:60",
4626                 "00010203040506.07-00:60",
4627
4628                 "00010203040506+15",
4629                 "00010203040506-15",
4630                 "00010203040506+14:60",
4631                 "00010203040506+1460",
4632                 "00010203040506-1460",
4633                 "00010203040506+00:60",
4634                 "00010203040506-00:60",
4635
4636                 "0001020304050.07",
4637                 "00010203040.07",
4638                 "000102030.07",
4639                 "0001020304050",
4640                 "00010203040",
4641                 "000102030",
4642         )):
4643             with self.assertRaises(DecodeError):
4644                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4645             data = data.replace(".", ",")
4646             with self.assertRaises(DecodeError):
4647                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4648
4649     @given(
4650         binary(
4651             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4652             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4653         ),
4654         binary(min_size=1, max_size=1),
4655         binary(
4656             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4657             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4658         ),
4659     )
4660     def test_junk(self, part0, part1, part2):
4661         junk = part0 + part1 + part2
4662         assume(not (set(junk) <= set(digits.encode("ascii"))))
4663         with self.assertRaises(DecodeError):
4664             GeneralizedTime().decode(
4665                 GeneralizedTime.tag_default +
4666                 len_encode(len(junk)) +
4667                 junk
4668             )
4669
4670     @given(
4671         binary(
4672             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4673             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4674         ),
4675         binary(min_size=1, max_size=1),
4676         binary(
4677             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4678             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4679         ),
4680     )
4681     def test_junk_dm(self, part0, part1, part2):
4682         junk = part0 + part1 + part2
4683         assume(not (set(junk) <= set(digits.encode("ascii"))))
4684         with self.assertRaises(DecodeError):
4685             GeneralizedTime().decode(
4686                 GeneralizedTime.tag_default +
4687                 len_encode(len(junk)) +
4688                 junk
4689             )
4690
4691     def test_ns_fractions(self):
4692         GeneralizedTime(b"20010101000000.000001Z")
4693         with self.assertRaisesRegex(DecodeError, "only microsecond fractions"):
4694             GeneralizedTime(b"20010101000000.0000001Z")
4695
4696     def test_non_pure_integers(self):
4697         for data in ((
4698                 # b"20000102030405Z,
4699                 b"+2000102030405Z",
4700                 b"2000+102030405Z",
4701                 b"200001+2030405Z",
4702                 b"20000102+30405Z",
4703                 b"2000010203+405Z",
4704                 b"200001020304+5Z",
4705                 b"20000102030405.+6Z",
4706                 b"20000102030405.-6Z",
4707                 b"_2000102030405Z",
4708                 b"2000_102030405Z",
4709                 b"200001_2030405Z",
4710                 b"20000102_30405Z",
4711                 b"2000010203_405Z",
4712                 b"200001020304_5Z",
4713                 b"20000102030405._6Z",
4714                 b"20000102030405.6_Z",
4715                 b" 2000102030405Z",
4716                 b"2000 102030405Z",
4717                 b"200001 2030405Z",
4718                 b"20000102 30405Z",
4719                 b"2000010203 405Z",
4720                 b"200001020304 5Z",
4721                 b"20000102030405. 6Z",
4722                 b"200 0102030405Z",
4723                 b"20001 02030405Z",
4724                 b"2000012 030405Z",
4725                 b"200001023 0405Z",
4726                 b"20000102034 05Z",
4727                 b"2000010203045 Z",
4728                 b"20000102030405.6 Z",
4729         )):
4730             with self.assertRaises(DecodeError):
4731                 GeneralizedTime(data)
4732
4733     def test_aware(self):
4734         with self.assertRaisesRegex(ValueError, "only naive"):
4735             GeneralizedTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
4736
4737
4738 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
4739     base_klass = UTCTime
4740     omit_ms = True
4741     min_datetime = datetime(2000, 1, 1)
4742     max_datetime = datetime(2049, 12, 31)
4743     evgen_mode_skip_value = False
4744
4745     def additional_symmetric_check(self, value, obj_encoded):
4746         pass
4747
4748     def test_repr_not_ready(self):
4749         str(GeneralizedTime())
4750         repr(UTCTime())
4751
4752     def test_x690_vector_valid(self):
4753         for data in ((
4754                 b"920521000000Z",
4755                 b"920622123421Z",
4756                 b"920722132100Z",
4757         )):
4758             UTCTime(data)
4759
4760     def test_x690_vector_invalid(self):
4761         for data in ((
4762                 b"920520240000Z",
4763                 b"9207221321Z",
4764         )):
4765             with self.assertRaises(DecodeError) as err:
4766                 UTCTime(data)
4767             repr(err.exception)
4768
4769     def test_go_vectors_invalid(self):
4770         for data in ((
4771                 b"a10506234540Z",
4772                 b"91a506234540Z",
4773                 b"9105a6234540Z",
4774                 b"910506a34540Z",
4775                 b"910506334a40Z",
4776                 b"91050633444aZ",
4777                 b"910506334461Z",
4778                 b"910506334400Za",
4779                 b"000100000000Z",
4780                 b"101302030405Z",
4781                 b"100002030405Z",
4782                 b"100100030405Z",
4783                 b"100132030405Z",
4784                 b"100231030405Z",
4785                 b"100102240405Z",
4786                 b"100102036005Z",
4787                 b"100102030460Z",
4788                 b"-100102030410Z",
4789                 b"10-0102030410Z",
4790                 b"10-0002030410Z",
4791                 b"1001-02030410Z",
4792                 b"100102-030410Z",
4793                 b"10010203-0410Z",
4794                 b"1001020304-10Z",
4795                 # These ones are INVALID in *DER*, but accepted
4796                 # by Go's encoding/asn1
4797                 b"910506164540-0700",
4798                 b"910506164540+0730",
4799                 b"9105062345Z",
4800                 b"5105062345Z",
4801         )):
4802             with self.assertRaises(DecodeError) as err:
4803                 UTCTime(data)
4804             repr(err.exception)
4805
4806     def test_go_vectors_valid(self):
4807         self.assertEqual(
4808             UTCTime(b"910506234540Z").todatetime(),
4809             datetime(1991, 5, 6, 23, 45, 40, 0),
4810         )
4811
4812     def test_non_pure_integers(self):
4813         for data in ((
4814                 # b"000102030405Z",
4815                 b"+10102030405Z",
4816                 b"00+102030405Z",
4817                 b"0001+2030405Z",
4818                 b"000102+30405Z",
4819                 b"00010203+405Z",
4820                 b"0001020304+5Z",
4821                 b"_10102030405Z",
4822                 b"00_102030405Z",
4823                 b"0001_2030405Z",
4824                 b"000102_30405Z",
4825                 b"00010203_405Z",
4826                 b"0001020304_5Z",
4827                 b"00010203045_Z",
4828                 b" 10102030405Z",
4829                 b"00 102030405Z",
4830                 b"0001 2030405Z",
4831                 b"000102 30405Z",
4832                 b"00010203 405Z",
4833                 b"0001020304 5Z",
4834                 b"1 0102030405Z",
4835                 b"001 02030405Z",
4836                 b"00012 030405Z",
4837                 b"0001023 0405Z",
4838                 b"000102034 05Z",
4839                 b"00010203045 Z",
4840         )):
4841             with self.assertRaises(DecodeError):
4842                 UTCTime(data)
4843
4844     def test_x680_vector_valid_ber(self):
4845         for data, dt in ((
4846                 (b"8201021200Z", datetime(1982, 1, 2, 12)),
4847                 (b"8201020700-0500", datetime(1982, 1, 2, 12)),
4848                 (b"0101021200Z", datetime(2001, 1, 2, 12)),
4849                 (b"0101020700-0500", datetime(2001, 1, 2, 12)),
4850         )):
4851             data_der = UTCTime.tag_default + len_encode(len(data)) + data
4852             obj = UTCTime().decod(data_der, ctx={"bered": True})
4853             self.assertEqual(obj, dt)
4854             self.assertEqual(obj.todatetime(), dt)
4855             self.assertTrue(obj.ber_encoded)
4856             self.assertTrue(obj.bered)
4857             self.assertEqual(obj.ber_raw, data)
4858             self.assertNotEqual(obj.encode(), data_der)
4859             repr(obj)
4860
4861     def test_go_vectors_valid_ber(self):
4862         for data in ((
4863                 b"910506164540-0700",
4864                 b"910506164540+0730",
4865                 b"9105062345Z",
4866                 b"5105062345Z",
4867         )):
4868             data = UTCTime.tag_default + len_encode(len(data)) + data
4869             obj = UTCTime().decod(data, ctx={"bered": True})
4870             self.assertTrue(obj.ber_encoded)
4871             self.assertTrue(obj.bered)
4872             self.assertNotEqual(obj.encode(), data)
4873             repr(obj)
4874
4875     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4876     @given(data_strategy())
4877     def test_valid_ber(self, d):
4878         year = d.draw(integers(min_value=0, max_value=99))
4879         month = d.draw(integers(min_value=1, max_value=12))
4880         day = d.draw(integers(min_value=1, max_value=28))
4881         hours = d.draw(integers(min_value=0, max_value=23))
4882         minute = d.draw(integers(min_value=0, max_value=59))
4883         data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
4884         dt = datetime(
4885             year + (2000 if year < 50 else 1900),
4886             month,
4887             day,
4888             hours,
4889             minute,
4890         )
4891         dered = False
4892         if d.draw(booleans()):
4893             dered = True
4894             seconds = d.draw(integers(min_value=0, max_value=59))
4895             data += "%02d" % seconds
4896             dt += timedelta(seconds=seconds)
4897         if d.draw(booleans()):
4898             data += "Z"
4899         else:
4900             dered = False
4901             offset_hour = d.draw(integers(min_value=0, max_value=13))
4902             offset_minute = d.draw(integers(min_value=0, max_value=59))
4903             offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
4904             if d.draw(booleans()):
4905                 dt += offset
4906                 data += "-"
4907             else:
4908                 dt -= offset
4909                 data += "+"
4910             data += "%02d%02d" % (offset_hour, offset_minute)
4911         data = data.encode("ascii")
4912         data_der = UTCTime.tag_default + len_encode(len(data)) + data
4913         obj = UTCTime().decod(data_der, ctx={"bered": True})
4914         self.assertEqual(obj, dt)
4915         self.assertEqual(obj.todatetime(), dt)
4916         self.assertEqual(obj.ber_encoded, not dered)
4917         self.assertEqual(obj.bered, not dered)
4918         self.assertEqual(obj.ber_raw, None if dered else data)
4919         self.assertEqual(obj.encode() == data_der, dered)
4920         repr(obj)
4921         bytes(obj)
4922         str(obj)
4923
4924     def test_invalid_ber(self):
4925         for data in ((
4926                 # b"0001020304Z",
4927                 b"-101020304Z",
4928                 b"00-1020304Z",
4929                 b"0001-20304Z",
4930                 b"000102-304Z",
4931                 b"000102-104Z",
4932                 b"00000203-4Z",
4933                 b"+101020304Z",
4934                 b"00+1020304Z",
4935                 b"0001+20304Z",
4936                 b"000102+304Z",
4937                 b"000102+104Z",
4938                 b"00000203+4Z",
4939                 b" 101020304Z",
4940                 b"00 1020304Z",
4941                 b"0001 20304Z",
4942                 b"000102 304Z",
4943                 b"000102 104Z",
4944                 b"00000203 4Z",
4945                 b"1 01020304Z",
4946                 b"001 020304Z",
4947                 b"00012 0304Z",
4948                 b"0001023 04Z",
4949                 b"0001021 04Z",
4950                 b"000002034 Z",
4951                 b"0013020304Z",
4952                 b"0001000304Z",
4953                 b"0001320304Z",
4954                 b"0001022404Z",
4955                 b"0001020360Z",
4956                 b"0002300304Z",
4957                 b"0001020304",
4958                 b"0001020304T",
4959                 b"0001020304+",
4960                 b"0001020304-",
4961                 b"0001020304+0",
4962                 b"0001020304+00",
4963                 b"0001020304+000",
4964                 b"0001020304+000Z",
4965                 b"0001020304+0000Z",
4966                 b"0001020304+-101",
4967                 b"0001020304+01-1",
4968                 b"0001020304+0060",
4969                 b"0001020304+1401",
4970                 b"5001010000+0001",
4971                 b"000102030Z",
4972                 b"0001020Z",
4973         )):
4974             with self.assertRaises(DecodeError):
4975                 UTCTime(data, ctx={"bered": True})
4976             data = data[:8] + data[8+2:]
4977             with self.assertRaises(DecodeError):
4978                 UTCTime(data, ctx={"bered": True})
4979
4980         for data in ((
4981                 # b"000102030405Z",
4982                 b"-10102030405Z",
4983                 b"00-102030405Z",
4984                 b"0001-2030405Z",
4985                 b"000102-30405Z",
4986                 b"000102-10405Z",
4987                 b"00000203-405Z",
4988                 b"0000020304-5Z",
4989                 b"+10102030405Z",
4990                 b"00+102030405Z",
4991                 b"0001+2030405Z",
4992                 b"000102+30405Z",
4993                 b"000102+10405Z",
4994                 b"00000203+405Z",
4995                 b"0000020304+5Z",
4996                 b" 10102030405Z",
4997                 b"00 102030405Z",
4998                 b"0001 2030405Z",
4999                 b"000102 30405Z",
5000                 b"000102 10405Z",
5001                 b"00000203 405Z",
5002                 b"0000020304 5Z",
5003                 b"1 0102030405Z",
5004                 b"001 02030405Z",
5005                 b"00012 030405Z",
5006                 b"0001023 0405Z",
5007                 b"0001021 0405Z",
5008                 b"000002034 05Z",
5009                 b"00000203045 Z",
5010                 b"001302030405Z",
5011                 b"000100030405Z",
5012                 b"000132030405Z",
5013                 b"000102240405Z",
5014                 b"000102036005Z",
5015                 b"000230030405Z",
5016                 b"000102030460Z",
5017                 b"000102030405",
5018                 b"000102030405T",
5019                 b"000102030405+",
5020                 b"000102030405-",
5021                 b"000102030405+0",
5022                 b"000102030405+00",
5023                 b"000102030405+000",
5024                 b"000102030405+000Z",
5025                 b"000102030405+0000Z",
5026                 b"000102030405+-101",
5027                 b"000102030405+01-1",
5028                 b"000102030405+0060",
5029                 b"000102030405+1401",
5030                 b"500101000002+0003",
5031         )):
5032             with self.assertRaises(DecodeError):
5033                 UTCTime(data, ctx={"bered": True})
5034
5035     @given(integers(min_value=0, max_value=49))
5036     def test_pre50(self, year):
5037         self.assertEqual(
5038             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
5039             2000 + year,
5040         )
5041
5042     @given(integers(min_value=50, max_value=99))
5043     def test_post50(self, year):
5044         self.assertEqual(
5045             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
5046             1900 + year,
5047         )
5048
5049     @given(
5050         binary(
5051             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5052             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5053         ),
5054         binary(min_size=1, max_size=1),
5055         binary(
5056             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5057             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5058         ),
5059     )
5060     def test_junk(self, part0, part1, part2):
5061         junk = part0 + part1 + part2
5062         assume(not (set(junk) <= set(digits.encode("ascii"))))
5063         with self.assertRaises(DecodeError):
5064             UTCTime().decode(
5065                 UTCTime.tag_default +
5066                 len_encode(len(junk)) +
5067                 junk
5068             )
5069
5070     def test_aware(self):
5071         with self.assertRaisesRegex(ValueError, "only naive"):
5072             UTCTime(datetime(2000, 1, 1, 1, tzinfo=UTC))
5073
5074     def test_raises_if_no_dateutil(self):
5075         with patch("pyderasn.tzUTC", new="missing"):
5076             with self.assertRaisesRegex(NotImplementedError, "dateutil"):
5077                 UTCTime(datetime.now()).totzdatetime()
5078
5079     def test_tzinfo_gives_datetime_with_tzutc_tzinfo(self):
5080         self.assertEqual(UTCTime(datetime.now()).totzdatetime().tzinfo, UTC)
5081
5082
5083 @composite
5084 def tlv_value_strategy(draw):
5085     tag_num = draw(integers(min_value=1))
5086     data = draw(binary())
5087     return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
5088
5089
5090 @composite
5091 def any_values_strategy(draw, do_expl=False):
5092     value = draw(one_of(none(), tlv_value_strategy()))
5093     expl = None
5094     if do_expl:
5095         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5096     optional = draw(one_of(none(), booleans()))
5097     _decoded = (
5098         draw(integers(min_value=0)),
5099         draw(integers(min_value=0)),
5100         draw(integers(min_value=0)),
5101     )
5102     return (value, expl, optional, _decoded)
5103
5104
5105 class AnyInherited(Any):
5106     pass
5107
5108
5109 class TestAny(CommonMixin, TestCase):
5110     base_klass = Any
5111
5112     def test_invalid_value_type(self):
5113         with self.assertRaises(InvalidValueType) as err:
5114             Any(123)
5115         repr(err.exception)
5116
5117     @given(booleans())
5118     def test_optional(self, optional):
5119         obj = Any(optional=optional)
5120         self.assertEqual(obj.optional, optional)
5121
5122     @given(tlv_value_strategy())
5123     def test_ready(self, value):
5124         obj = Any()
5125         self.assertFalse(obj.ready)
5126         repr(obj)
5127         list(obj.pps())
5128         pprint(obj, big_blobs=True, with_decode_path=True)
5129         with self.assertRaises(ObjNotReady) as err:
5130             obj.encode()
5131         repr(err.exception)
5132         with self.assertRaises(ObjNotReady) as err:
5133             encode2pass(obj)
5134         obj = Any(value)
5135         self.assertTrue(obj.ready)
5136         repr(obj)
5137         list(obj.pps())
5138         pprint(obj, big_blobs=True, with_decode_path=True)
5139
5140     @given(integers())
5141     def test_basic(self, value):
5142         integer_encoded = Integer(value).encode()
5143         for obj in (
5144                 Any(integer_encoded),
5145                 Any(Integer(value)),
5146                 Any(Any(Integer(value))),
5147         ):
5148             self.assertSequenceEqual(bytes(obj), integer_encoded)
5149             self.assertEqual(
5150                 obj.decode(obj.encode())[0].vlen,
5151                 len(integer_encoded),
5152             )
5153             repr(obj)
5154             list(obj.pps())
5155             pprint(obj, big_blobs=True, with_decode_path=True)
5156             self.assertSequenceEqual(obj.encode(), integer_encoded)
5157
5158     @given(tlv_value_strategy(), tlv_value_strategy())
5159     def test_comparison(self, value1, value2):
5160         for klass in (Any, AnyInherited):
5161             obj1 = klass(value1)
5162             obj2 = klass(value2)
5163             self.assertEqual(obj1 == obj2, value1 == value2)
5164             self.assertEqual(obj1 != obj2, value1 != value2)
5165             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
5166
5167     @given(data_strategy())
5168     def test_call(self, d):
5169         for klass in (Any, AnyInherited):
5170             (
5171                 value_initial,
5172                 expl_initial,
5173                 optional_initial,
5174                 _decoded_initial,
5175             ) = d.draw(any_values_strategy())
5176             obj_initial = klass(
5177                 value_initial,
5178                 expl_initial,
5179                 optional_initial or False,
5180                 _decoded_initial,
5181             )
5182             (
5183                 value,
5184                 expl,
5185                 optional,
5186                 _decoded,
5187             ) = d.draw(any_values_strategy(do_expl=True))
5188             obj = obj_initial(value, expl, optional)
5189             if obj.ready:
5190                 value_expected = None if value is None else value
5191                 self.assertEqual(obj, value_expected)
5192             self.assertEqual(obj.expl_tag, expl or expl_initial)
5193             if obj.default is None:
5194                 optional = optional_initial if optional is None else optional
5195                 optional = False if optional is None else optional
5196             self.assertEqual(obj.optional, optional)
5197
5198     def test_simultaneous_impl_expl(self):
5199         # override it, as Any does not have implicit tag
5200         pass
5201
5202     def test_decoded(self):
5203         # override it, as Any does not have implicit tag
5204         pass
5205
5206     @given(any_values_strategy())
5207     def test_copy(self, values):
5208         for klass in (Any, AnyInherited):
5209             obj = klass(*values)
5210             for copy_func in copy_funcs:
5211                 obj_copied = copy_func(obj)
5212                 self.assert_copied_basic_fields(obj, obj_copied)
5213                 self.assertEqual(obj._value, obj_copied._value)
5214
5215     @given(binary().map(OctetString))
5216     def test_stripped(self, value):
5217         obj = Any(value)
5218         with self.assertRaises(NotEnoughData):
5219             obj.decode(obj.encode()[:-1])
5220
5221     @given(
5222         tlv_value_strategy(),
5223         integers(min_value=1).map(tag_ctxc),
5224     )
5225     def test_stripped_expl(self, value, tag_expl):
5226         obj = Any(value, expl=tag_expl)
5227         with self.assertRaises(NotEnoughData):
5228             obj.decode(obj.encode()[:-1])
5229
5230     @given(
5231         integers(min_value=31),
5232         integers(min_value=0),
5233         decode_path_strat,
5234     )
5235     def test_bad_tag(self, tag, offset, decode_path):
5236         with self.assertRaises(DecodeError) as err:
5237             Any().decode(
5238                 tag_encode(tag)[:-1],
5239                 offset=offset,
5240                 decode_path=decode_path,
5241             )
5242         repr(err.exception)
5243         self.assertEqual(err.exception.offset, offset)
5244         self.assertEqual(err.exception.decode_path, decode_path)
5245
5246     @given(
5247         integers(min_value=128),
5248         integers(min_value=0),
5249         decode_path_strat,
5250     )
5251     def test_bad_len(self, l, offset, decode_path):
5252         with self.assertRaises(DecodeError) as err:
5253             Any().decode(
5254                 Any.tag_default + len_encode(l)[:-1],
5255                 offset=offset,
5256                 decode_path=decode_path,
5257             )
5258         repr(err.exception)
5259         self.assertEqual(err.exception.offset, offset)
5260         self.assertEqual(err.exception.decode_path, decode_path)
5261
5262     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5263     @given(
5264         any_values_strategy(),
5265         integers().map(lambda x: Integer(x).encode()),
5266         integers(min_value=1).map(tag_ctxc),
5267         integers(min_value=0),
5268         binary(max_size=5),
5269         decode_path_strat,
5270     )
5271     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
5272         for klass in (Any, AnyInherited):
5273             _, _, optional, _decoded = values
5274             obj = klass(value=value, optional=optional, _decoded=_decoded)
5275             repr(obj)
5276             list(obj.pps())
5277             pprint(obj, big_blobs=True, with_decode_path=True)
5278             self.assertFalse(obj.expled)
5279             tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5280             self.assertEqual(obj.tag_order, (tag_class, tag_num))
5281             obj_encoded = obj.encode()
5282             self.assertEqual(encode2pass(obj), obj_encoded)
5283             obj_expled = obj(value, expl=tag_expl)
5284             self.assertTrue(obj_expled.expled)
5285             tag_class, _, tag_num = tag_decode(tag_expl)
5286             self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5287             repr(obj_expled)
5288             list(obj_expled.pps())
5289             pprint(obj_expled, big_blobs=True, with_decode_path=True)
5290             obj_expled_encoded = obj_expled.encode()
5291             ctx_copied = deepcopy(ctx_dummy)
5292             obj_decoded, tail = obj_expled.decode(
5293                 obj_expled_encoded + tail_junk,
5294                 offset=offset,
5295                 ctx=ctx_copied,
5296             )
5297             self.assertDictEqual(ctx_copied, ctx_dummy)
5298             repr(obj_decoded)
5299             list(obj_decoded.pps())
5300             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5301             self.assertEqual(tail, tail_junk)
5302             self.assertEqual(obj_decoded, obj_expled)
5303             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
5304             self.assertEqual(bytes(obj_decoded), bytes(obj))
5305             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5306             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5307             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5308             self.assertEqual(
5309                 obj_decoded.expl_llen,
5310                 len(len_encode(len(obj_encoded))),
5311             )
5312             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5313             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5314             self.assertEqual(
5315                 obj_decoded.offset,
5316                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5317             )
5318             self.assertEqual(obj_decoded.expl_offset, offset)
5319             self.assertEqual(obj_decoded.tlen, 0)
5320             self.assertEqual(obj_decoded.llen, 0)
5321             self.assertEqual(obj_decoded.vlen, len(value))
5322             assert_exceeding_data(
5323                 self,
5324                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5325                 tail_junk,
5326             )
5327
5328             evgens = list(obj_expled.decode_evgen(
5329                 obj_expled_encoded + tail_junk,
5330                 offset=offset,
5331                 decode_path=decode_path,
5332                 ctx=ctx_copied,
5333             ))
5334             self.assertEqual(len(evgens), 1)
5335             _decode_path, obj, tail = evgens[0]
5336             self.assertSequenceEqual(tail, tail_junk)
5337             self.assertEqual(_decode_path, decode_path)
5338             self.assertEqual(obj.expl_offset, offset)
5339             repr(obj)
5340             list(obj.pps())
5341
5342     @given(
5343         integers(min_value=1).map(tag_ctxc),
5344         integers(min_value=0, max_value=3),
5345         integers(min_value=0),
5346         decode_path_strat,
5347         binary(),
5348     )
5349     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
5350         chunk = Boolean(False, expl=expl).encode()
5351         encoded = (
5352             OctetString.tag_default +
5353             LENINDEF +
5354             b"".join([chunk] * chunks) +
5355             EOC
5356         )
5357         with self.assertRaises(LenIndefForm):
5358             Any().decode(
5359                 encoded + junk,
5360                 offset=offset,
5361                 decode_path=decode_path,
5362             )
5363         obj, tail = Any().decode(
5364             encoded + junk,
5365             offset=offset,
5366             decode_path=decode_path,
5367             ctx={"bered": True},
5368         )
5369         self.assertSequenceEqual(tail, junk)
5370         self.assertEqual(obj.offset, offset)
5371         self.assertEqual(obj.tlvlen, len(encoded))
5372         self.assertTrue(obj.lenindef)
5373         self.assertFalse(obj.ber_encoded)
5374         self.assertTrue(obj.bered)
5375         obj = copy(obj)
5376         self.assertTrue(obj.lenindef)
5377         self.assertFalse(obj.ber_encoded)
5378         self.assertTrue(obj.bered)
5379         repr(obj)
5380         list(obj.pps())
5381         pprint(obj, big_blobs=True, with_decode_path=True)
5382         with self.assertRaises(NotEnoughData) as err:
5383             Any().decode(
5384                 encoded[:-1],
5385                 offset=offset,
5386                 decode_path=decode_path,
5387                 ctx={"bered": True},
5388             )
5389         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
5390         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
5391
5392         class SeqOf(SequenceOf):
5393             schema = Boolean(expl=expl)
5394
5395         class Seq(Sequence):
5396             schema = (
5397                 ("type", ObjectIdentifier(defines=((("value",), {
5398                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
5399                 }),))),
5400                 ("value", Any()),
5401             )
5402         seq = Seq((
5403             ("type", ObjectIdentifier("1.2.3")),
5404             ("value", Any(encoded)),
5405         ))
5406         seq_encoded = seq.encode()
5407         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5408         self.assertIsNotNone(seq_decoded["value"].defined)
5409         repr(seq_decoded)
5410         list(seq_decoded.pps())
5411         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5412         self.assertTrue(seq_decoded.bered)
5413         self.assertFalse(seq_decoded["type"].bered)
5414         self.assertTrue(seq_decoded["value"].bered)
5415
5416         chunk = chunk[:-1] + b"\x01"
5417         chunks = b"".join([chunk] * (chunks + 1))
5418         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
5419         seq = Seq((
5420             ("type", ObjectIdentifier("1.2.3")),
5421             ("value", Any(encoded)),
5422         ))
5423         seq_encoded = seq.encode()
5424         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5425         self.assertIsNotNone(seq_decoded["value"].defined)
5426         repr(seq_decoded)
5427         list(seq_decoded.pps())
5428         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5429         self.assertTrue(seq_decoded.bered)
5430         self.assertFalse(seq_decoded["type"].bered)
5431         self.assertTrue(seq_decoded["value"].bered)
5432
5433
5434 @composite
5435 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
5436     if schema is None:
5437         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
5438         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
5439             one_of(
5440                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
5441                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
5442             ),
5443             min_size=len(names),
5444             max_size=len(names),
5445         ))]
5446         schema = [
5447             (name, Integer(**tag_kwargs))
5448             for name, tag_kwargs in zip(names, tags)
5449         ]
5450     value = None
5451     if value_required or draw(booleans()):
5452         value = draw(tuples(
5453             sampled_from([name for name, _ in schema]),
5454             integers().map(Integer),
5455         ))
5456     expl = None
5457     if do_expl:
5458         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5459     default = draw(one_of(
5460         none(),
5461         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
5462     ))
5463     optional = draw(one_of(none(), booleans()))
5464     _decoded = (
5465         draw(integers(min_value=0)),
5466         draw(integers(min_value=0)),
5467         draw(integers(min_value=0)),
5468     )
5469     return (schema, value, expl, default, optional, _decoded)
5470
5471
5472 class ChoiceInherited(Choice):
5473     pass
5474
5475
5476 class TestChoice(CommonMixin, TestCase):
5477     class Wahl(Choice):
5478         schema = (("whatever", Boolean()),)
5479     base_klass = Wahl
5480
5481     def test_schema_required(self):
5482         with self.assertRaisesRegex(ValueError, "schema must be specified"):
5483             Choice()
5484
5485     def test_impl_forbidden(self):
5486         with self.assertRaisesRegex(ValueError, "no implicit tag allowed"):
5487             Choice(impl=b"whatever")
5488
5489     def test_invalid_value_type(self):
5490         with self.assertRaises(InvalidValueType) as err:
5491             self.base_klass(123)
5492         repr(err.exception)
5493         with self.assertRaises(ObjUnknown) as err:
5494             self.base_klass(("whenever", Boolean(False)))
5495         repr(err.exception)
5496         with self.assertRaises(InvalidValueType) as err:
5497             self.base_klass(("whatever", Integer(123)))
5498         repr(err.exception)
5499
5500     @given(booleans())
5501     def test_optional(self, optional):
5502         obj = self.base_klass(
5503             default=self.base_klass(("whatever", Boolean(False))),
5504             optional=optional,
5505         )
5506         self.assertTrue(obj.optional)
5507
5508     @given(booleans())
5509     def test_ready(self, value):
5510         obj = self.base_klass()
5511         self.assertFalse(obj.ready)
5512         repr(obj)
5513         list(obj.pps())
5514         pprint(obj, big_blobs=True, with_decode_path=True)
5515         self.assertIsNone(obj["whatever"])
5516         with self.assertRaises(ObjNotReady) as err:
5517             obj.encode()
5518         repr(err.exception)
5519         with self.assertRaises(ObjNotReady) as err:
5520             encode2pass(obj)
5521         obj["whatever"] = Boolean()
5522         self.assertFalse(obj.ready)
5523         repr(obj)
5524         list(obj.pps())
5525         pprint(obj, big_blobs=True, with_decode_path=True)
5526         obj["whatever"] = Boolean(value)
5527         self.assertTrue(obj.ready)
5528         repr(obj)
5529         list(obj.pps())
5530         pprint(obj, big_blobs=True, with_decode_path=True)
5531
5532     @given(booleans(), booleans())
5533     def test_comparison(self, value1, value2):
5534         class WahlInherited(self.base_klass):
5535             pass
5536         for klass in (self.base_klass, WahlInherited):
5537             obj1 = klass(("whatever", Boolean(value1)))
5538             obj2 = klass(("whatever", Boolean(value2)))
5539             self.assertEqual(obj1 == obj2, value1 == value2)
5540             self.assertEqual(obj1 != obj2, value1 != value2)
5541             self.assertEqual(obj1 == obj2._value, value1 == value2)
5542             self.assertFalse(obj1 == obj2._value[1])
5543
5544     @given(data_strategy())
5545     def test_call(self, d):
5546         for klass in (Choice, ChoiceInherited):
5547             (
5548                 schema_initial,
5549                 value_initial,
5550                 expl_initial,
5551                 default_initial,
5552                 optional_initial,
5553                 _decoded_initial,
5554             ) = d.draw(choice_values_strategy())
5555
5556             class Wahl(klass):
5557                 schema = schema_initial
5558             obj_initial = Wahl(
5559                 value=value_initial,
5560                 expl=expl_initial,
5561                 default=default_initial,
5562                 optional=optional_initial or False,
5563                 _decoded=_decoded_initial,
5564             )
5565             (
5566                 _,
5567                 value,
5568                 expl,
5569                 default,
5570                 optional,
5571                 _decoded,
5572             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
5573             obj = obj_initial(value, expl, default, optional)
5574             if obj.ready:
5575                 value_expected = default if value is None else value
5576                 value_expected = (
5577                     default_initial if value_expected is None
5578                     else value_expected
5579                 )
5580                 self.assertEqual(obj.choice, value_expected[0])
5581                 self.assertEqual(obj.value, int(value_expected[1]))
5582             self.assertEqual(obj.expl_tag, expl or expl_initial)
5583             default_expect = default_initial if default is None else default
5584             if default_expect is not None:
5585                 self.assertEqual(obj.default.choice, default_expect[0])
5586                 self.assertEqual(obj.default.value, int(default_expect[1]))
5587             if obj.default is None:
5588                 optional = optional_initial if optional is None else optional
5589                 optional = False if optional is None else optional
5590             else:
5591                 optional = True
5592             self.assertEqual(obj.optional, optional)
5593             self.assertEqual(obj.specs, obj_initial.specs)
5594
5595     def test_simultaneous_impl_expl(self):
5596         # override it, as Any does not have implicit tag
5597         pass
5598
5599     def test_decoded(self):
5600         # override it, as Any does not have implicit tag
5601         pass
5602
5603     @given(choice_values_strategy())
5604     def test_copy(self, values):
5605         _schema, value, expl, default, optional, _decoded = values
5606
5607         class Wahl(self.base_klass):
5608             schema = _schema
5609         register_class(Wahl)
5610         obj = Wahl(
5611             value=value,
5612             expl=expl,
5613             default=default,
5614             optional=optional or False,
5615             _decoded=_decoded,
5616         )
5617         for copy_func in copy_funcs:
5618             obj_copied = copy_func(obj)
5619             self.assertIsNone(obj.tag)
5620             self.assertIsNone(obj_copied.tag)
5621             # hack for assert_copied_basic_fields
5622             obj.tag = "whatever"
5623             obj_copied.tag = "whatever"
5624             self.assert_copied_basic_fields(obj, obj_copied)
5625             obj.tag = None
5626             self.assertEqual(obj._value, obj_copied._value)
5627             self.assertEqual(obj.specs, obj_copied.specs)
5628
5629     @given(booleans())
5630     def test_stripped(self, value):
5631         obj = self.base_klass(("whatever", Boolean(value)))
5632         with self.assertRaises(NotEnoughData):
5633             obj.decode(obj.encode()[:-1])
5634
5635     @given(
5636         booleans(),
5637         integers(min_value=1).map(tag_ctxc),
5638     )
5639     def test_stripped_expl(self, value, tag_expl):
5640         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
5641         with self.assertRaises(NotEnoughData):
5642             obj.decode(obj.encode()[:-1])
5643
5644     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5645     @given(data_strategy())
5646     def test_symmetric(self, d):
5647         _schema, value, _, default, optional, _decoded = d.draw(
5648             choice_values_strategy(value_required=True)
5649         )
5650         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5651         offset = d.draw(integers(min_value=0))
5652         tail_junk = d.draw(binary(max_size=5))
5653         decode_path = d.draw(decode_path_strat)
5654
5655         class Wahl(self.base_klass):
5656             schema = _schema
5657         obj = Wahl(
5658             value=value,
5659             default=default,
5660             optional=optional,
5661             _decoded=_decoded,
5662         )
5663         repr(obj)
5664         list(obj.pps())
5665         pprint(obj, big_blobs=True, with_decode_path=True)
5666         self.assertFalse(obj.expled)
5667         self.assertEqual(obj.tag_order, obj.value.tag_order)
5668         obj_encoded = obj.encode()
5669         self.assertEqual(encode2pass(obj), obj_encoded)
5670         obj_expled = obj(value, expl=tag_expl)
5671         self.assertTrue(obj_expled.expled)
5672         tag_class, _, tag_num = tag_decode(tag_expl)
5673         self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5674         repr(obj_expled)
5675         list(obj_expled.pps())
5676         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5677         obj_expled_encoded = obj_expled.encode()
5678         ctx_copied = deepcopy(ctx_dummy)
5679         obj_decoded, tail = obj_expled.decode(
5680             obj_expled_encoded + tail_junk,
5681             offset=offset,
5682             ctx=ctx_copied,
5683         )
5684         self.assertDictEqual(ctx_copied, ctx_dummy)
5685         repr(obj_decoded)
5686         list(obj_decoded.pps())
5687         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5688         self.assertEqual(tail, tail_junk)
5689         self.assertEqual(obj_decoded, obj_expled)
5690         self.assertEqual(obj_decoded.choice, obj_expled.choice)
5691         self.assertEqual(obj_decoded.value, obj_expled.value)
5692         self.assertEqual(obj_decoded.choice, obj.choice)
5693         self.assertEqual(obj_decoded.value, obj.value)
5694         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5695         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5696         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5697         self.assertEqual(
5698             obj_decoded.expl_llen,
5699             len(len_encode(len(obj_encoded))),
5700         )
5701         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5702         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5703         self.assertEqual(
5704             obj_decoded.offset,
5705             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5706         )
5707         self.assertEqual(obj_decoded.expl_offset, offset)
5708         self.assertSequenceEqual(
5709             obj_expled_encoded[
5710                 obj_decoded.value.fulloffset - offset:
5711                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
5712             ],
5713             obj_encoded,
5714         )
5715         assert_exceeding_data(
5716             self,
5717             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5718             tail_junk,
5719         )
5720
5721         evgens = list(obj_expled.decode_evgen(
5722             obj_expled_encoded + tail_junk,
5723             offset=offset,
5724             decode_path=decode_path,
5725             ctx=ctx_copied,
5726         ))
5727         self.assertEqual(len(evgens), 2)
5728         _decode_path, obj, tail = evgens[0]
5729         self.assertEqual(_decode_path, decode_path + (obj_decoded.choice,))
5730         _decode_path, obj, tail = evgens[1]
5731         self.assertSequenceEqual(tail, tail_junk)
5732         self.assertEqual(_decode_path, decode_path)
5733         self.assertEqual(obj.expl_offset, offset)
5734         repr(obj)
5735         list(obj.pps())
5736
5737     @given(integers())
5738     def test_set_get(self, value):
5739         class Wahl(Choice):
5740             schema = (
5741                 ("erste", Boolean()),
5742                 ("zweite", Integer()),
5743             )
5744         obj = Wahl()
5745         with self.assertRaises(ObjUnknown) as err:
5746             obj["whatever"] = "whenever"
5747         with self.assertRaises(InvalidValueType) as err:
5748             obj["zweite"] = Boolean(False)
5749         obj["zweite"] = Integer(value)
5750         repr(err.exception)
5751         with self.assertRaises(ObjUnknown) as err:
5752             obj["whatever"]
5753         repr(err.exception)
5754         self.assertIsNone(obj["erste"])
5755         self.assertEqual(obj["zweite"], Integer(value))
5756
5757     def test_tag_mismatch(self):
5758         class Wahl(Choice):
5759             schema = (
5760                 ("erste", Boolean()),
5761             )
5762         int_encoded = Integer(123).encode()
5763         bool_encoded = Boolean(False).encode()
5764         obj = Wahl()
5765         obj.decode(bool_encoded)
5766         with self.assertRaises(TagMismatch):
5767             obj.decode(int_encoded)
5768
5769     def test_tag_mismatch_underlying(self):
5770         class SeqOfBoolean(SequenceOf):
5771             schema = Boolean()
5772
5773         class SeqOfInteger(SequenceOf):
5774             schema = Integer()
5775
5776         class Wahl(Choice):
5777             schema = (
5778                 ("erste", SeqOfBoolean()),
5779             )
5780
5781         int_encoded = SeqOfInteger((Integer(123),)).encode()
5782         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
5783         obj = Wahl()
5784         obj.decode(bool_encoded)
5785         with self.assertRaises(TagMismatch) as err:
5786             obj.decode(int_encoded)
5787         self.assertEqual(err.exception.decode_path, ("erste", "0"))
5788
5789
5790 @composite
5791 def seq_values_strategy(draw, seq_klass, do_expl=False):
5792     value = None
5793     if draw(booleans()):
5794         value = seq_klass()
5795         value._value = draw(dictionaries(
5796             integers(),
5797             one_of(
5798                 booleans().map(Boolean),
5799                 integers().map(Integer),
5800             ),
5801         ))
5802     schema = None
5803     if draw(booleans()):
5804         schema = list(draw(dictionaries(
5805             integers(),
5806             one_of(
5807                 booleans().map(Boolean),
5808                 integers().map(Integer),
5809             ),
5810         )).items())
5811     impl = None
5812     expl = None
5813     if do_expl:
5814         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5815     else:
5816         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5817     default = None
5818     if draw(booleans()):
5819         default = seq_klass()
5820         default._value = draw(dictionaries(
5821             integers(),
5822             one_of(
5823                 booleans().map(Boolean),
5824                 integers().map(Integer),
5825             ),
5826         ))
5827     optional = draw(one_of(none(), booleans()))
5828     _decoded = (
5829         draw(integers(min_value=0)),
5830         draw(integers(min_value=0)),
5831         draw(integers(min_value=0)),
5832     )
5833     return (value, schema, impl, expl, default, optional, _decoded)
5834
5835
5836 @composite
5837 def sequence_strategy(draw, seq_klass):
5838     inputs = draw(lists(
5839         one_of(
5840             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
5841             tuples(just(Integer), integers(), one_of(none(), integers())),
5842         ),
5843         max_size=6,
5844     ))
5845     tags = draw(sets(
5846         integers(min_value=1),
5847         min_size=len(inputs),
5848         max_size=len(inputs),
5849     ))
5850     inits = [
5851         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5852         for tag, expled in zip(tags, draw(lists(
5853             booleans(),
5854             min_size=len(inputs),
5855             max_size=len(inputs),
5856         )))
5857     ]
5858     empties = []
5859     for i, optional in enumerate(draw(lists(
5860             sampled_from(("required", "optional", "empty")),
5861             min_size=len(inputs),
5862             max_size=len(inputs),
5863     ))):
5864         if optional in ("optional", "empty"):
5865             inits[i]["optional"] = True
5866         if optional == "empty":
5867             empties.append(i)
5868     empties = set(empties)
5869     names = list(draw(sets(
5870         text_printable,
5871         min_size=len(inputs),
5872         max_size=len(inputs),
5873     )))
5874     schema = []
5875     for i, (klass, value, default) in enumerate(inputs):
5876         schema.append((names[i], klass(default=default, **inits[i])))
5877     seq_name = draw(text_letters())
5878     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5879     seq = Seq()
5880     expects = []
5881     for i, (klass, value, default) in enumerate(inputs):
5882         name = names[i]
5883         _, spec = schema[i]
5884         expect = {
5885             "name": name,
5886             "optional": False,
5887             "presented": False,
5888             "default_value": None if spec.default is None else default,
5889             "value": None,
5890         }
5891         if i in empties:
5892             expect["optional"] = True
5893         else:
5894             expect["presented"] = True
5895             expect["value"] = value
5896             if spec.optional:
5897                 expect["optional"] = True
5898             if default is not None and default == value:
5899                 expect["presented"] = False
5900             seq[name] = klass(value)
5901         expects.append(expect)
5902     return seq, expects
5903
5904
5905 @composite
5906 def sequences_strategy(draw, seq_klass):
5907     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
5908     inits = [
5909         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5910         for tag, expled in zip(tags, draw(lists(
5911             booleans(),
5912             min_size=len(tags),
5913             max_size=len(tags),
5914         )))
5915     ]
5916     defaulted = set(
5917         i for i, is_default in enumerate(draw(lists(
5918             booleans(),
5919             min_size=len(tags),
5920             max_size=len(tags),
5921         ))) if is_default
5922     )
5923     names = list(draw(sets(
5924         text_printable,
5925         min_size=len(tags),
5926         max_size=len(tags),
5927     )))
5928     seq_expectses = draw(lists(
5929         sequence_strategy(seq_klass=seq_klass),
5930         min_size=len(tags),
5931         max_size=len(tags),
5932     ))
5933     seqs = [seq for seq, _ in seq_expectses]
5934     schema = []
5935     for i, (name, seq) in enumerate(zip(names, seqs)):
5936         schema.append((
5937             name,
5938             seq(default=(seq if i in defaulted else None), **inits[i]),
5939         ))
5940     seq_name = draw(text_letters())
5941     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5942     seq_outer = Seq()
5943     expect_outers = []
5944     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
5945         expect = {
5946             "name": name,
5947             "expects": expects_inner,
5948             "presented": False,
5949         }
5950         seq_outer[name] = seq_inner
5951         if seq_outer.specs[name].default is None:
5952             expect["presented"] = True
5953         expect_outers.append(expect)
5954     return seq_outer, expect_outers
5955
5956
5957 class SeqMixin(object):
5958     def test_invalid_value_type(self):
5959         with self.assertRaises(InvalidValueType) as err:
5960             self.base_klass(123)
5961         repr(err.exception)
5962
5963     def test_invalid_value_type_set(self):
5964         class Seq(self.base_klass):
5965             schema = (("whatever", Boolean()),)
5966         seq = Seq()
5967         with self.assertRaises(InvalidValueType) as err:
5968             seq["whatever"] = Integer(123)
5969         repr(err.exception)
5970
5971     @given(booleans())
5972     def test_optional(self, optional):
5973         obj = self.base_klass(default=self.base_klass(), optional=optional)
5974         self.assertTrue(obj.optional)
5975
5976     @given(data_strategy())
5977     def test_ready(self, d):
5978         ready = {
5979             str(i): v for i, v in enumerate(d.draw(lists(
5980                 booleans(),
5981                 min_size=1,
5982                 max_size=3,
5983             )))
5984         }
5985         non_ready = {
5986             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
5987                 booleans(),
5988                 min_size=1,
5989                 max_size=3,
5990             )))
5991         }
5992         schema_input = []
5993         for name in d.draw(permutations(
5994                 list(ready.keys()) + list(non_ready.keys()),
5995         )):
5996             schema_input.append((name, Boolean()))
5997
5998         class Seq(self.base_klass):
5999             schema = tuple(schema_input)
6000         seq = Seq()
6001         for name in ready.keys():
6002             seq[name]
6003             seq[name] = Boolean()
6004         self.assertFalse(seq.ready)
6005         repr(seq)
6006         list(seq.pps())
6007         pprint(seq, big_blobs=True, with_decode_path=True)
6008         for name, value in ready.items():
6009             seq[name] = Boolean(value)
6010         self.assertFalse(seq.ready)
6011         repr(seq)
6012         list(seq.pps())
6013         pprint(seq, big_blobs=True, with_decode_path=True)
6014         with self.assertRaises(ObjNotReady) as err:
6015             seq.encode()
6016         repr(err.exception)
6017         with self.assertRaises(ObjNotReady) as err:
6018             encode2pass(seq)
6019         for name, value in non_ready.items():
6020             seq[name] = Boolean(value)
6021         self.assertTrue(seq.ready)
6022         repr(seq)
6023         list(seq.pps())
6024         pprint(seq, big_blobs=True, with_decode_path=True)
6025
6026     @given(data_strategy())
6027     def test_call(self, d):
6028         class SeqInherited(self.base_klass):
6029             pass
6030         for klass in (self.base_klass, SeqInherited):
6031             (
6032                 value_initial,
6033                 schema_initial,
6034                 impl_initial,
6035                 expl_initial,
6036                 default_initial,
6037                 optional_initial,
6038                 _decoded_initial,
6039             ) = d.draw(seq_values_strategy(seq_klass=klass))
6040             obj_initial = klass(
6041                 value_initial,
6042                 schema_initial,
6043                 impl_initial,
6044                 expl_initial,
6045                 default_initial,
6046                 optional_initial or False,
6047                 _decoded_initial,
6048             )
6049             (
6050                 value,
6051                 _,
6052                 impl,
6053                 expl,
6054                 default,
6055                 optional,
6056                 _decoded,
6057             ) = d.draw(seq_values_strategy(
6058                 seq_klass=klass,
6059                 do_expl=impl_initial is None,
6060             ))
6061             obj = obj_initial(value, impl, expl, default, optional)
6062             value_expected = default if value is None else value
6063             value_expected = (
6064                 default_initial if value_expected is None
6065                 else value_expected
6066             )
6067             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
6068             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6069             self.assertEqual(obj.expl_tag, expl or expl_initial)
6070             self.assertEqual(
6071                 {} if obj.default is None else obj.default._value,
6072                 getattr(default_initial if default is None else default, "_value", {}),
6073             )
6074             if obj.default is None:
6075                 optional = optional_initial if optional is None else optional
6076                 optional = False if optional is None else optional
6077             else:
6078                 optional = True
6079             self.assertEqual(list(obj.specs.items()), schema_initial or [])
6080             self.assertEqual(obj.optional, optional)
6081
6082     @given(data_strategy())
6083     def test_copy(self, d):
6084         class SeqInherited(self.base_klass):
6085             pass
6086         register_class(SeqInherited)
6087         for klass in (self.base_klass, SeqInherited):
6088             values = d.draw(seq_values_strategy(seq_klass=klass))
6089             obj = klass(*values)
6090             for copy_func in copy_funcs:
6091                 obj_copied = copy_func(obj)
6092                 self.assert_copied_basic_fields(obj, obj_copied)
6093                 self.assertEqual(obj.specs, obj_copied.specs)
6094                 self.assertEqual(obj._value, obj_copied._value)
6095
6096     @given(data_strategy())
6097     def test_stripped(self, d):
6098         value = d.draw(integers())
6099         tag_impl = tag_encode(d.draw(integers(min_value=1)))
6100
6101         class Seq(self.base_klass):
6102             impl = tag_impl
6103             schema = (("whatever", Integer()),)
6104         seq = Seq()
6105         seq["whatever"] = Integer(value)
6106         with self.assertRaises(NotEnoughData):
6107             seq.decode(seq.encode()[:-1])
6108
6109     @given(data_strategy())
6110     def test_stripped_expl(self, d):
6111         value = d.draw(integers())
6112         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
6113
6114         class Seq(self.base_klass):
6115             expl = tag_expl
6116             schema = (("whatever", Integer()),)
6117         seq = Seq()
6118         seq["whatever"] = Integer(value)
6119         with self.assertRaises(NotEnoughData):
6120             seq.decode(seq.encode()[:-1])
6121
6122     @given(integers(min_value=3), binary(min_size=2))
6123     def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
6124         junk = tag_encode(junk_tag_num) + junk
6125         try:
6126             _, _, len_encoded = tag_strip(memoryview(junk))
6127             len_decode(len_encoded)
6128         except Exception:
6129             assume(True)
6130         else:
6131             assume(False)
6132
6133         class Seq(self.base_klass):
6134             schema = (
6135                 ("whatever", Integer()),
6136                 ("junk", Any()),
6137                 ("whenever", Integer()),
6138             )
6139         seq = Seq()
6140         seq["whatever"] = Integer(123)
6141         seq["junk"] = Any(junk)
6142         seq["whenever"] = Integer(123)
6143         with self.assertRaises(DecodeError):
6144             seq.decode(seq.encode())
6145
6146     @given(
6147         integers(min_value=31),
6148         integers(min_value=0),
6149         decode_path_strat,
6150     )
6151     def test_bad_tag(self, tag, offset, decode_path):
6152         with self.assertRaises(DecodeError) as err:
6153             self.base_klass().decode(
6154                 tag_encode(tag)[:-1],
6155                 offset=offset,
6156                 decode_path=decode_path,
6157             )
6158         repr(err.exception)
6159         self.assertEqual(err.exception.offset, offset)
6160         self.assertEqual(err.exception.decode_path, decode_path)
6161
6162     @given(
6163         integers(min_value=128),
6164         integers(min_value=0),
6165         decode_path_strat,
6166     )
6167     def test_bad_len(self, l, offset, decode_path):
6168         with self.assertRaises(DecodeError) as err:
6169             self.base_klass().decode(
6170                 self.base_klass.tag_default + len_encode(l)[:-1],
6171                 offset=offset,
6172                 decode_path=decode_path,
6173             )
6174         repr(err.exception)
6175         self.assertEqual(err.exception.offset, offset)
6176         self.assertEqual(err.exception.decode_path, decode_path)
6177
6178     def _assert_expects(self, seq, expects):
6179         for expect in expects:
6180             self.assertEqual(
6181                 seq.specs[expect["name"]].optional,
6182                 expect["optional"],
6183             )
6184             if expect["default_value"] is not None:
6185                 self.assertEqual(
6186                     seq.specs[expect["name"]].default,
6187                     expect["default_value"],
6188                 )
6189             if expect["presented"]:
6190                 self.assertIn(expect["name"], seq)
6191                 self.assertEqual(seq[expect["name"]], expect["value"])
6192
6193     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6194     @given(data_strategy())
6195     def test_symmetric(self, d):
6196         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
6197         tail_junk = d.draw(binary(max_size=5))
6198         decode_path = d.draw(decode_path_strat)
6199         self.assertTrue(seq.ready)
6200         self.assertFalse(seq.decoded)
6201         self._assert_expects(seq, expects)
6202         repr(seq)
6203         list(seq.pps())
6204         pprint(seq, big_blobs=True, with_decode_path=True)
6205         self.assertTrue(seq.ready)
6206         seq_encoded = seq.encode()
6207         self.assertEqual(encode2pass(seq), seq_encoded)
6208         seq_encoded_cer = encode_cer(seq)
6209         self.assertNotEqual(seq_encoded_cer, seq_encoded)
6210         self.assertSequenceEqual(
6211             seq.decod(seq_encoded_cer, ctx={"bered": True}).encode(),
6212             seq_encoded,
6213         )
6214         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
6215         self.assertFalse(seq_decoded.lenindef)
6216         self.assertFalse(seq_decoded.ber_encoded)
6217         self.assertFalse(seq_decoded.bered)
6218
6219         t, _, lv = tag_strip(seq_encoded)
6220         _, _, v = len_decode(lv)
6221         seq_encoded_lenindef = t + LENINDEF + v + EOC
6222         with self.assertRaises(DecodeError):
6223             seq.decode(seq_encoded_lenindef)
6224         ctx_copied = deepcopy(ctx_dummy)
6225         ctx_copied["bered"] = True
6226         seq_decoded_lenindef, tail_lenindef = seq.decode(
6227             seq_encoded_lenindef + tail_junk,
6228             ctx=ctx_copied,
6229         )
6230         del ctx_copied["bered"]
6231         self.assertDictEqual(ctx_copied, ctx_dummy)
6232         self.assertTrue(seq_decoded_lenindef.lenindef)
6233         self.assertTrue(seq_decoded_lenindef.bered)
6234         seq_decoded_lenindef = copy(seq_decoded_lenindef)
6235         self.assertTrue(seq_decoded_lenindef.lenindef)
6236         self.assertTrue(seq_decoded_lenindef.bered)
6237         with self.assertRaises(DecodeError):
6238             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
6239         with self.assertRaises(DecodeError):
6240             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
6241         repr(seq_decoded_lenindef)
6242         list(seq_decoded_lenindef.pps())
6243         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
6244         self.assertTrue(seq_decoded_lenindef.ready)
6245
6246         for decoded, decoded_tail, encoded in (
6247                 (seq_decoded, tail, seq_encoded),
6248                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
6249         ):
6250             self.assertEqual(decoded_tail, tail_junk)
6251             self._assert_expects(decoded, expects)
6252             self.assertEqual(seq, decoded)
6253             self.assertEqual(decoded.encode(), seq_encoded)
6254             self.assertEqual(decoded.tlvlen, len(encoded))
6255             for expect in expects:
6256                 if not expect["presented"]:
6257                     self.assertNotIn(expect["name"], decoded)
6258                     continue
6259                 self.assertIn(expect["name"], decoded)
6260                 obj = decoded[expect["name"]]
6261                 self.assertTrue(obj.decoded)
6262                 offset = obj.expl_offset if obj.expled else obj.offset
6263                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6264                 self.assertSequenceEqual(
6265                     seq_encoded[offset:offset + tlvlen],
6266                     obj.encode(),
6267                 )
6268
6269             evgens = list(seq.decode_evgen(
6270                 encoded + decoded_tail,
6271                 decode_path=decode_path,
6272                 ctx={"bered": True},
6273             ))
6274             self.assertEqual(len(evgens), len(list(decoded._values_for_encoding())) + 1)
6275             for _decode_path, obj, _ in evgens[:-1]:
6276                 self.assertEqual(_decode_path[:-1], decode_path)
6277                 repr(obj)
6278                 list(obj.pps())
6279             _decode_path, obj, tail = evgens[-1]
6280             self.assertEqual(_decode_path, decode_path)
6281             repr(obj)
6282             list(obj.pps())
6283
6284         assert_exceeding_data(
6285             self,
6286             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
6287             tail_junk,
6288         )
6289
6290     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6291     @given(data_strategy())
6292     def test_symmetric_with_seq(self, d):
6293         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
6294         self.assertTrue(seq.ready)
6295         seq_encoded = seq.encode()
6296         self.assertEqual(encode2pass(seq), seq_encoded)
6297         seq_decoded, tail = seq.decode(seq_encoded)
6298         self.assertEqual(tail, b"")
6299         self.assertTrue(seq.ready)
6300         self.assertEqual(seq, seq_decoded)
6301         self.assertEqual(seq_decoded.encode(), seq_encoded)
6302         for expect_outer in expect_outers:
6303             if not expect_outer["presented"]:
6304                 self.assertNotIn(expect_outer["name"], seq_decoded)
6305                 continue
6306             self.assertIn(expect_outer["name"], seq_decoded)
6307             obj = seq_decoded[expect_outer["name"]]
6308             self.assertTrue(obj.decoded)
6309             offset = obj.expl_offset if obj.expled else obj.offset
6310             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6311             self.assertSequenceEqual(
6312                 seq_encoded[offset:offset + tlvlen],
6313                 obj.encode(),
6314             )
6315             self._assert_expects(obj, expect_outer["expects"])
6316
6317     @given(data_strategy())
6318     def test_default_disappears(self, d):
6319         _schema = list(d.draw(dictionaries(
6320             text_letters(),
6321             sets(integers(), min_size=2, max_size=2),
6322             min_size=1,
6323         )).items())
6324
6325         class Seq(self.base_klass):
6326             schema = [
6327                 (n, Integer(default=d))
6328                 for n, (_, d) in _schema
6329             ]
6330         seq = Seq()
6331         for name, (value, _) in _schema:
6332             seq[name] = Integer(value)
6333         self.assertEqual(len(seq._value), len(_schema))
6334         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
6335         self.assertGreater(len(seq.encode()), len(empty_seq))
6336         for name, (_, default) in _schema:
6337             seq[name] = Integer(default)
6338         self.assertEqual(len(seq._value), 0)
6339         self.assertSequenceEqual(seq.encode(), empty_seq)
6340
6341     @given(data_strategy())
6342     def test_encoded_default_not_accepted(self, d):
6343         _schema = list(d.draw(dictionaries(
6344             text_letters(),
6345             integers(),
6346             min_size=1,
6347         )).items())
6348         tags = [tag_encode(tag) for tag in d.draw(sets(
6349             integers(min_value=1),
6350             min_size=len(_schema),
6351             max_size=len(_schema),
6352         ))]
6353
6354         class Wahl(Choice):
6355             schema = (("int", Integer()),)
6356
6357         class SeqWithoutDefault(self.base_klass):
6358             schema = [
6359                 (n, Wahl(expl=t))
6360                 for (n, _), t in zip(_schema, tags)
6361             ]
6362         seq_without_default = SeqWithoutDefault()
6363         for name, value in _schema:
6364             seq_without_default[name] = Wahl(("int", Integer(value)))
6365         seq_encoded = seq_without_default.encode()
6366         seq_without_default.decode(seq_encoded)
6367         self.assertEqual(
6368             len(list(seq_without_default.decode_evgen(seq_encoded))),
6369             len(_schema) * 2 + 1,
6370         )
6371
6372         class SeqWithDefault(self.base_klass):
6373             schema = [
6374                 (n, Wahl(default=Wahl(("int", Integer(v))), expl=t))
6375                 for (n, v), t in zip(_schema, tags)
6376             ]
6377         seq_with_default = SeqWithDefault()
6378         with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
6379             seq_with_default.decode(seq_encoded)
6380         with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
6381             list(seq_with_default.decode_evgen(seq_encoded))
6382         for ctx in ({"bered": True}, {"allow_default_values": True}):
6383             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
6384             self.assertTrue(seq_decoded.ber_encoded)
6385             self.assertTrue(seq_decoded.bered)
6386             seq_decoded = copy(seq_decoded)
6387             self.assertTrue(seq_decoded.ber_encoded)
6388             self.assertTrue(seq_decoded.bered)
6389             for name, value in _schema:
6390                 self.assertEqual(seq_decoded[name], seq_with_default[name])
6391                 self.assertEqual(seq_decoded[name].value, value)
6392             self.assertEqual(
6393                 len(list(seq_with_default.decode_evgen(seq_encoded, ctx=ctx))),
6394                 len(_schema) + 1,
6395             )
6396
6397         seq_without_default = SeqWithoutDefault()
6398         for name, value in _schema:
6399             seq_without_default[name] = Wahl(("int", Integer(value + 1)))
6400         seq_encoded = seq_without_default.encode()
6401         seq_with_default.decode(seq_encoded)
6402         self.assertEqual(
6403             len(list(seq_with_default.decode_evgen(seq_encoded))),
6404             len(_schema) + 1,
6405         )
6406
6407     @given(data_strategy())
6408     def test_missing_from_spec(self, d):
6409         names = list(d.draw(sets(text_letters(), min_size=2)))
6410         tags = [tag_encode(tag) for tag in d.draw(sets(
6411             integers(min_value=1),
6412             min_size=len(names),
6413             max_size=len(names),
6414         ))]
6415         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
6416
6417         class SeqFull(self.base_klass):
6418             schema = [(n, Integer(impl=t)) for n, t in names_tags]
6419         seq_full = SeqFull()
6420         for i, name in enumerate(names):
6421             seq_full[name] = Integer(i)
6422         seq_encoded = seq_full.encode()
6423         altered = names_tags[:-2] + names_tags[-1:]
6424
6425         class SeqMissing(self.base_klass):
6426             schema = [(n, Integer(impl=t)) for n, t in altered]
6427         seq_missing = SeqMissing()
6428         with self.assertRaises(TagMismatch):
6429             seq_missing.decode(seq_encoded)
6430         with self.assertRaises(TagMismatch):
6431             list(seq_missing.decode_evgen(seq_encoded))
6432
6433     def test_bered(self):
6434         class Seq(self.base_klass):
6435             schema = (("underlying", Boolean()),)
6436         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
6437         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6438         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6439         self.assertFalse(decoded.ber_encoded)
6440         self.assertFalse(decoded.lenindef)
6441         self.assertTrue(decoded.bered)
6442         decoded = copy(decoded)
6443         self.assertFalse(decoded.ber_encoded)
6444         self.assertFalse(decoded.lenindef)
6445         self.assertTrue(decoded.bered)
6446
6447         class Seq(self.base_klass):
6448             schema = (("underlying", OctetString()),)
6449         encoded = (
6450             tag_encode(form=TagFormConstructed, num=4) +
6451             LENINDEF +
6452             OctetString(b"whatever").encode() +
6453             EOC
6454         )
6455         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6456         with self.assertRaises(DecodeError):
6457             Seq().decode(encoded)
6458         with self.assertRaises(DecodeError):
6459             list(Seq().decode_evgen(encoded))
6460         list(Seq().decode_evgen(encoded, ctx={"bered": True}))
6461         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6462         self.assertFalse(decoded.ber_encoded)
6463         self.assertFalse(decoded.lenindef)
6464         self.assertTrue(decoded.bered)
6465         decoded = copy(decoded)
6466         self.assertFalse(decoded.ber_encoded)
6467         self.assertFalse(decoded.lenindef)
6468         self.assertTrue(decoded.bered)
6469
6470
6471 class TestSequence(SeqMixin, CommonMixin, TestCase):
6472     base_klass = Sequence
6473
6474     @given(
6475         integers(),
6476         binary(min_size=1),
6477     )
6478     def test_remaining(self, value, junk):
6479         class Seq(Sequence):
6480             schema = (
6481                 ("whatever", Integer()),
6482             )
6483         int_encoded = Integer(value).encode()
6484         junked = b"".join((
6485             Sequence.tag_default,
6486             len_encode(len(int_encoded + junk)),
6487             int_encoded + junk,
6488         ))
6489         with self.assertRaisesRegex(DecodeError, "remaining"):
6490             Seq().decode(junked)
6491
6492     @given(sets(text_letters(), min_size=2))
6493     def test_obj_unknown(self, names):
6494         missing = names.pop()
6495
6496         class Seq(Sequence):
6497             schema = [(n, Boolean()) for n in names]
6498         seq = Seq()
6499         with self.assertRaises(ObjUnknown) as err:
6500             seq[missing]
6501         repr(err.exception)
6502         with self.assertRaises(ObjUnknown) as err:
6503             seq[missing] = Boolean()
6504         repr(err.exception)
6505
6506     def test_x690_vector(self):
6507         class Seq(Sequence):
6508             schema = (
6509                 ("name", IA5String()),
6510                 ("ok", Boolean()),
6511             )
6512         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
6513         self.assertEqual(seq["name"], "Smith")
6514         self.assertEqual(seq["ok"], True)
6515
6516
6517 class TestSet(SeqMixin, CommonMixin, TestCase):
6518     base_klass = Set
6519
6520     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6521     @given(data_strategy())
6522     def test_sorted(self, d):
6523         class DummySeq(Sequence):
6524             schema = (("null", Null()),)
6525
6526         tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
6527         _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
6528         assume(any(i > dummy_seq_tag_num for i in tag_nums))
6529         tag_nums -= set([dummy_seq_tag_num])
6530         _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
6531         _schema.append(("seq", DummySeq()))
6532
6533         class Seq(Set):
6534             schema = d.draw(permutations(_schema))
6535         seq = Seq()
6536         for name, _ in _schema:
6537             if name != "seq":
6538                 seq[name] = OctetString(name.encode("ascii"))
6539         seq["seq"] = DummySeq((("null", Null()),))
6540
6541         seq_encoded = seq.encode()
6542         seq_decoded, _ = seq.decode(seq_encoded)
6543         seq_encoded_expected = []
6544         for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
6545             if tag_num == dummy_seq_tag_num:
6546                 seq_encoded_expected.append(seq["seq"].encode())
6547             else:
6548                 seq_encoded_expected.append(seq[str(tag_num)].encode())
6549         self.assertSequenceEqual(
6550             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6551             b"".join(seq_encoded_expected),
6552         )
6553
6554         encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
6555         encoded += seq["seq"].encode()
6556         seq_encoded = b"".join((
6557             Set.tag_default,
6558             len_encode(len(encoded)),
6559             encoded,
6560         ))
6561         with self.assertRaisesRegex(DecodeError, "unordered SET"):
6562             seq.decode(seq_encoded)
6563         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6564             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6565             self.assertTrue(seq_decoded.ber_encoded)
6566             self.assertTrue(seq_decoded.bered)
6567             seq_decoded = copy(seq_decoded)
6568             self.assertTrue(seq_decoded.ber_encoded)
6569             self.assertTrue(seq_decoded.bered)
6570
6571     def test_same_value_twice(self):
6572         class Seq(Set):
6573             schema = (
6574                 ("bool", Boolean()),
6575                 ("int", Integer()),
6576             )
6577
6578         encoded = b"".join((
6579             Integer(123).encode(),
6580             Integer(234).encode(),
6581             Boolean(True).encode(),
6582         ))
6583         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6584         with self.assertRaises(TagMismatch):
6585             Seq().decod(encoded, ctx={"allow_unordered_set": True})
6586
6587
6588 @composite
6589 def seqof_values_strategy(draw, schema=None, do_expl=False):
6590     if schema is None:
6591         schema = draw(sampled_from((Boolean(), Integer())))
6592     bound_min, bound_max = sorted(draw(sets(
6593         integers(min_value=0, max_value=10),
6594         min_size=2,
6595         max_size=2,
6596     )))
6597     if isinstance(schema, Boolean):
6598         values_generator = booleans().map(Boolean)
6599     elif isinstance(schema, Integer):
6600         values_generator = integers().map(Integer)
6601     values_generator = lists(
6602         values_generator,
6603         min_size=bound_min,
6604         max_size=bound_max,
6605     )
6606     values = draw(one_of(none(), values_generator))
6607     impl = None
6608     expl = None
6609     if do_expl:
6610         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6611     else:
6612         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6613     default = draw(one_of(none(), values_generator))
6614     optional = draw(one_of(none(), booleans()))
6615     _decoded = (
6616         draw(integers(min_value=0)),
6617         draw(integers(min_value=0)),
6618         draw(integers(min_value=0)),
6619     )
6620     return (
6621         schema,
6622         values,
6623         (bound_min, bound_max),
6624         impl,
6625         expl,
6626         default,
6627         optional,
6628         _decoded,
6629     )
6630
6631
6632 class SeqOfMixin(object):
6633     def test_invalid_value_type(self):
6634         with self.assertRaises(InvalidValueType) as err:
6635             self.base_klass(123)
6636         repr(err.exception)
6637
6638     def test_invalid_values_type(self):
6639         class SeqOf(self.base_klass):
6640             schema = Integer()
6641         with self.assertRaises(InvalidValueType) as err:
6642             SeqOf([Integer(123), Boolean(False), Integer(234)])
6643         repr(err.exception)
6644
6645     def test_schema_required(self):
6646         with self.assertRaisesRegex(ValueError, "schema must be specified"):
6647             self.base_klass.__mro__[1]()
6648
6649     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
6650     def test_comparison(self, value1, value2, tag1, tag2):
6651         class SeqOf(self.base_klass):
6652             schema = Boolean()
6653         obj1 = SeqOf([Boolean(value1)])
6654         obj2 = SeqOf([Boolean(value2)])
6655         self.assertEqual(obj1 == obj2, value1 == value2)
6656         self.assertEqual(obj1 != obj2, value1 != value2)
6657         self.assertEqual(obj1 == list(obj2), value1 == value2)
6658         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
6659         obj1 = SeqOf([Boolean(value1)], impl=tag1)
6660         obj2 = SeqOf([Boolean(value1)], impl=tag2)
6661         self.assertEqual(obj1 == obj2, tag1 == tag2)
6662         self.assertEqual(obj1 != obj2, tag1 != tag2)
6663
6664     @given(lists(booleans()))
6665     def test_iter(self, values):
6666         class SeqOf(self.base_klass):
6667             schema = Boolean()
6668         obj = SeqOf([Boolean(value) for value in values])
6669         self.assertEqual(len(obj), len(values))
6670         for i, value in enumerate(obj):
6671             self.assertEqual(value, values[i])
6672
6673     @given(data_strategy())
6674     def test_ready(self, d):
6675         ready = [Integer(v) for v in d.draw(lists(
6676             integers(),
6677             min_size=1,
6678             max_size=3,
6679         ))]
6680         non_ready = [
6681             Integer() for _ in
6682             range(d.draw(integers(min_value=1, max_value=5)))
6683         ]
6684
6685         class SeqOf(self.base_klass):
6686             schema = Integer()
6687         values = d.draw(permutations(ready + non_ready))
6688         seqof = SeqOf()
6689         for value in values:
6690             seqof.append(value)
6691         self.assertFalse(seqof.ready)
6692         repr(seqof)
6693         list(seqof.pps())
6694         pprint(seqof, big_blobs=True, with_decode_path=True)
6695         with self.assertRaises(ObjNotReady) as err:
6696             seqof.encode()
6697         repr(err.exception)
6698         with self.assertRaises(ObjNotReady) as err:
6699             encode2pass(seqof)
6700         for i, value in enumerate(values):
6701             self.assertEqual(seqof[i], value)
6702             if not seqof[i].ready:
6703                 seqof[i] = Integer(i)
6704         self.assertTrue(seqof.ready)
6705         repr(seqof)
6706         list(seqof.pps())
6707         pprint(seqof, big_blobs=True, with_decode_path=True)
6708
6709     def test_spec_mismatch(self):
6710         class SeqOf(self.base_klass):
6711             schema = Integer()
6712         seqof = SeqOf()
6713         seqof.append(Integer(123))
6714         with self.assertRaises(ValueError):
6715             seqof.append(Boolean(False))
6716         with self.assertRaises(ValueError):
6717             seqof[0] = Boolean(False)
6718
6719     @given(data_strategy())
6720     def test_bounds_satisfied(self, d):
6721         class SeqOf(self.base_klass):
6722             schema = Boolean()
6723         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
6724         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6725         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
6726         SeqOf(value=value, bounds=(bound_min, bound_max))
6727
6728     @given(data_strategy())
6729     def test_bounds_unsatisfied(self, d):
6730         class SeqOf(self.base_klass):
6731             schema = Boolean()
6732         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
6733         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6734         value = [Boolean(False)] * d.draw(integers(min_value=0, max_value=bound_min - 1))
6735         with self.assertRaises(BoundsError) as err:
6736             SeqOf(value=value, bounds=(bound_min, bound_max))
6737         repr(err.exception)
6738         with self.assertRaisesRegex(DecodeError, "bounds") as err:
6739             SeqOf(bounds=(bound_min, bound_max)).decode(
6740                 SeqOf(value).encode()
6741             )
6742         repr(err.exception)
6743         with self.assertRaisesRegex(DecodeError, "bounds") as err:
6744             SeqOf(bounds=(bound_min, bound_max)).decode(
6745                 encode2pass(SeqOf(value))
6746             )
6747         value = [Boolean(True)] * d.draw(integers(
6748             min_value=bound_max + 1,
6749             max_value=bound_max + 10,
6750         ))
6751         with self.assertRaises(BoundsError) as err:
6752             SeqOf(value=value, bounds=(bound_min, bound_max))
6753         repr(err.exception)
6754         with self.assertRaisesRegex(DecodeError, "bounds") as err:
6755             SeqOf(bounds=(bound_min, bound_max)).decode(
6756                 SeqOf(value).encode()
6757             )
6758         repr(err.exception)
6759         with self.assertRaisesRegex(DecodeError, "bounds") as err:
6760             SeqOf(bounds=(bound_min, bound_max)).decode(
6761                 encode2pass(SeqOf(value))
6762             )
6763
6764     @given(integers(min_value=1, max_value=10))
6765     def test_out_of_bounds(self, bound_max):
6766         class SeqOf(self.base_klass):
6767             schema = Integer()
6768             bounds = (0, bound_max)
6769         seqof = SeqOf()
6770         for _ in range(bound_max):
6771             seqof.append(Integer(123))
6772         with self.assertRaises(BoundsError):
6773             seqof.append(Integer(123))
6774
6775     @given(data_strategy())
6776     def test_call(self, d):
6777         (
6778             schema_initial,
6779             value_initial,
6780             bounds_initial,
6781             impl_initial,
6782             expl_initial,
6783             default_initial,
6784             optional_initial,
6785             _decoded_initial,
6786         ) = d.draw(seqof_values_strategy())
6787
6788         class SeqOf(self.base_klass):
6789             schema = schema_initial
6790         obj_initial = SeqOf(
6791             value=value_initial,
6792             bounds=bounds_initial,
6793             impl=impl_initial,
6794             expl=expl_initial,
6795             default=default_initial,
6796             optional=optional_initial or False,
6797             _decoded=_decoded_initial,
6798         )
6799         (
6800             _,
6801             value,
6802             bounds,
6803             impl,
6804             expl,
6805             default,
6806             optional,
6807             _decoded,
6808         ) = d.draw(seqof_values_strategy(
6809             schema=schema_initial,
6810             do_expl=impl_initial is None,
6811         ))
6812         if (default is None) and (obj_initial.default is not None):
6813             bounds = None
6814         if (
6815                 (bounds is None) and
6816                 (value is not None) and
6817                 (bounds_initial is not None) and
6818                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
6819         ):
6820             value = None
6821         if (
6822                 (bounds is None) and
6823                 (default is not None) and
6824                 (bounds_initial is not None) and
6825                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
6826         ):
6827             default = None
6828         obj = obj_initial(
6829             value=value,
6830             bounds=bounds,
6831             impl=impl,
6832             expl=expl,
6833             default=default,
6834             optional=optional,
6835         )
6836         if obj.ready:
6837             value_expected = default if value is None else value
6838             value_expected = (
6839                 default_initial if value_expected is None
6840                 else value_expected
6841             )
6842             value_expected = () if value_expected is None else value_expected
6843             self.assertEqual(obj, value_expected)
6844         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6845         self.assertEqual(obj.expl_tag, expl or expl_initial)
6846         self.assertEqual(
6847             obj.default,
6848             default_initial if default is None else default,
6849         )
6850         if obj.default is None:
6851             optional = optional_initial if optional is None else optional
6852             optional = False if optional is None else optional
6853         else:
6854             optional = True
6855         self.assertEqual(obj.optional, optional)
6856         self.assertEqual(
6857             (obj._bound_min, obj._bound_max),
6858             bounds or bounds_initial or (0, float("+inf")),
6859         )
6860
6861     @given(seqof_values_strategy())
6862     def test_copy(self, values):
6863         _schema, value, bounds, impl, expl, default, optional, _decoded = values
6864
6865         class SeqOf(self.base_klass):
6866             schema = _schema
6867         register_class(SeqOf)
6868         obj = SeqOf(
6869             value=value,
6870             bounds=bounds,
6871             impl=impl,
6872             expl=expl,
6873             default=default,
6874             optional=optional or False,
6875             _decoded=_decoded,
6876         )
6877         for copy_func in copy_funcs:
6878             obj_copied = copy_func(obj)
6879             self.assert_copied_basic_fields(obj, obj_copied)
6880             self.assertEqual(obj._bound_min, obj_copied._bound_min)
6881             self.assertEqual(obj._bound_max, obj_copied._bound_max)
6882             self.assertEqual(obj._value, obj_copied._value)
6883
6884     @given(
6885         lists(binary()),
6886         integers(min_value=1).map(tag_encode),
6887     )
6888     def test_stripped(self, values, tag_impl):
6889         class SeqOf(self.base_klass):
6890             schema = OctetString()
6891         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
6892         with self.assertRaises(NotEnoughData):
6893             obj.decode(obj.encode()[:-1])
6894
6895     @given(
6896         lists(binary()),
6897         integers(min_value=1).map(tag_ctxc),
6898     )
6899     def test_stripped_expl(self, values, tag_expl):
6900         class SeqOf(self.base_klass):
6901             schema = OctetString()
6902         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
6903         with self.assertRaises(NotEnoughData):
6904             obj.decode(obj.encode()[:-1])
6905
6906     @given(
6907         integers(min_value=31),
6908         integers(min_value=0),
6909         decode_path_strat,
6910     )
6911     def test_bad_tag(self, tag, offset, decode_path):
6912         with self.assertRaises(DecodeError) as err:
6913             self.base_klass().decode(
6914                 tag_encode(tag)[:-1],
6915                 offset=offset,
6916                 decode_path=decode_path,
6917             )
6918         repr(err.exception)
6919         self.assertEqual(err.exception.offset, offset)
6920         self.assertEqual(err.exception.decode_path, decode_path)
6921
6922     @given(
6923         integers(min_value=128),
6924         integers(min_value=0),
6925         decode_path_strat,
6926     )
6927     def test_bad_len(self, l, offset, decode_path):
6928         with self.assertRaises(DecodeError) as err:
6929             self.base_klass().decode(
6930                 self.base_klass.tag_default + len_encode(l)[:-1],
6931                 offset=offset,
6932                 decode_path=decode_path,
6933             )
6934         repr(err.exception)
6935         self.assertEqual(err.exception.offset, offset)
6936         self.assertEqual(err.exception.decode_path, decode_path)
6937
6938     @given(binary(min_size=1))
6939     def test_tag_mismatch(self, impl):
6940         assume(impl != self.base_klass.tag_default)
6941         with self.assertRaises(TagMismatch):
6942             self.base_klass(impl=impl).decode(self.base_klass().encode())
6943
6944     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6945     @given(
6946         seqof_values_strategy(schema=Integer()),
6947         lists(integers().map(Integer)),
6948         integers(min_value=1).map(tag_ctxc),
6949         integers(min_value=0),
6950         binary(max_size=5),
6951         decode_path_strat,
6952     )
6953     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
6954         _, _, _, _, _, default, optional, _decoded = values
6955
6956         class SeqOf(self.base_klass):
6957             schema = Integer()
6958         obj = SeqOf(
6959             value=value,
6960             default=default,
6961             optional=optional,
6962             _decoded=_decoded,
6963         )
6964         repr(obj)
6965         list(obj.pps())
6966         pprint(obj, big_blobs=True, with_decode_path=True)
6967         self.assertFalse(obj.expled)
6968         obj_encoded = obj.encode()
6969         self.assertEqual(encode2pass(obj), obj_encoded)
6970         obj_encoded_cer = encode_cer(obj)
6971         self.assertNotEqual(obj_encoded_cer, obj_encoded)
6972         self.assertSequenceEqual(
6973             obj.decod(obj_encoded_cer, ctx={"bered": True}).encode(),
6974             obj_encoded,
6975         )
6976         obj_expled = obj(value, expl=tag_expl)
6977         self.assertTrue(obj_expled.expled)
6978         repr(obj_expled)
6979         list(obj_expled.pps())
6980         pprint(obj_expled, big_blobs=True, with_decode_path=True)
6981         obj_expled_encoded = obj_expled.encode()
6982         ctx_copied = deepcopy(ctx_dummy)
6983         obj_decoded, tail = obj_expled.decode(
6984             obj_expled_encoded + tail_junk,
6985             offset=offset,
6986             ctx=ctx_copied,
6987         )
6988         self.assertDictEqual(ctx_copied, ctx_dummy)
6989         repr(obj_decoded)
6990         list(obj_decoded.pps())
6991         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
6992         self.assertEqual(tail, tail_junk)
6993         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
6994         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
6995         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
6996         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
6997         self.assertEqual(
6998             obj_decoded.expl_llen,
6999             len(len_encode(len(obj_encoded))),
7000         )
7001         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
7002         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
7003         self.assertEqual(
7004             obj_decoded.offset,
7005             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
7006         )
7007         self.assertEqual(obj_decoded.expl_offset, offset)
7008         for obj_inner in obj_decoded:
7009             self.assertIn(obj_inner, obj_decoded)
7010             self.assertSequenceEqual(
7011                 obj_inner.encode(),
7012                 obj_expled_encoded[
7013                     obj_inner.offset - offset:
7014                     obj_inner.offset + obj_inner.tlvlen - offset
7015                 ],
7016             )
7017
7018         t, _, lv = tag_strip(obj_encoded)
7019         _, _, v = len_decode(lv)
7020         obj_encoded_lenindef = t + LENINDEF + v + EOC
7021         with self.assertRaises(DecodeError):
7022             obj.decode(obj_encoded_lenindef)
7023         obj_decoded_lenindef, tail_lenindef = obj.decode(
7024             obj_encoded_lenindef + tail_junk,
7025             ctx={"bered": True},
7026         )
7027         self.assertTrue(obj_decoded_lenindef.lenindef)
7028         self.assertTrue(obj_decoded_lenindef.bered)
7029         obj_decoded_lenindef = copy(obj_decoded_lenindef)
7030         self.assertTrue(obj_decoded_lenindef.lenindef)
7031         self.assertTrue(obj_decoded_lenindef.bered)
7032         repr(obj_decoded_lenindef)
7033         list(obj_decoded_lenindef.pps())
7034         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
7035         self.assertEqual(tail_lenindef, tail_junk)
7036         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
7037         with self.assertRaises(DecodeError):
7038             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
7039         with self.assertRaises(DecodeError):
7040             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
7041
7042         evgens = list(obj.decode_evgen(
7043             obj_encoded_lenindef + tail_junk,
7044             decode_path=decode_path,
7045             ctx={"bered": True},
7046         ))
7047         self.assertEqual(len(evgens), len(obj_decoded_lenindef) + 1)
7048         for i, (_decode_path, obj, _) in enumerate(evgens[:-1]):
7049             self.assertEqual(_decode_path, decode_path + (str(i),))
7050             repr(obj)
7051             list(obj.pps())
7052         _decode_path, obj, tail = evgens[-1]
7053         self.assertEqual(_decode_path, decode_path)
7054         repr(obj)
7055         list(obj.pps())
7056
7057         assert_exceeding_data(
7058             self,
7059             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
7060             tail_junk,
7061         )
7062
7063     def test_bered(self):
7064         class SeqOf(self.base_klass):
7065             schema = Boolean()
7066         encoded = Boolean(False).encode()
7067         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
7068         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
7069         with self.assertRaises(DecodeError):
7070             SeqOf().decode(encoded)
7071         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
7072         self.assertFalse(decoded.ber_encoded)
7073         self.assertFalse(decoded.lenindef)
7074         self.assertTrue(decoded.bered)
7075         decoded = copy(decoded)
7076         self.assertFalse(decoded.ber_encoded)
7077         self.assertFalse(decoded.lenindef)
7078         self.assertTrue(decoded.bered)
7079
7080         class SeqOf(self.base_klass):
7081             schema = OctetString()
7082         encoded = OctetString(b"whatever").encode()
7083         encoded += (
7084             tag_encode(form=TagFormConstructed, num=4) +
7085             LENINDEF +
7086             OctetString(b"whatever").encode() +
7087             EOC
7088         )
7089         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
7090         with self.assertRaises(DecodeError):
7091             SeqOf().decode(encoded)
7092         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
7093         self.assertFalse(decoded.ber_encoded)
7094         self.assertFalse(decoded.lenindef)
7095         self.assertTrue(decoded.bered)
7096         decoded = copy(decoded)
7097         self.assertFalse(decoded.ber_encoded)
7098         self.assertFalse(decoded.lenindef)
7099         self.assertTrue(decoded.bered)
7100
7101
7102 class TestSequenceOf(SeqOfMixin, CommonMixin, TestCase):
7103     class SeqOf(SequenceOf):
7104         schema = "whatever"
7105     base_klass = SeqOf
7106
7107     def _test_symmetric_compare_objs(self, obj1, obj2):
7108         self.assertEqual(obj1, obj2)
7109         self.assertSequenceEqual(list(obj1), list(obj2))
7110
7111     def test_iterator_pickling(self):
7112         class SeqOf(SequenceOf):
7113             schema = Integer()
7114         register_class(SeqOf)
7115         seqof = SeqOf()
7116         pickle_dumps(seqof)
7117         seqof = seqof(iter(range(10)))
7118         with self.assertRaisesRegex(ValueError, "iterator"):
7119             pickle_dumps(seqof)
7120
7121     def test_iterator_bounds(self):
7122         class SeqOf(SequenceOf):
7123             schema = Integer()
7124             bounds = (10, 20)
7125         seqof = None
7126
7127         def gen(n):
7128             for i in range(n):
7129                 yield Integer(i)
7130         for n in (9, 21):
7131             seqof = SeqOf(gen(n))
7132             self.assertTrue(seqof.ready)
7133             with self.assertRaises(BoundsError):
7134                 seqof.encode()
7135             self.assertFalse(seqof.ready)
7136             seqof = seqof(gen(n))
7137             self.assertTrue(seqof.ready)
7138             with self.assertRaises(BoundsError):
7139                 encode_cer(seqof)
7140             self.assertFalse(seqof.ready)
7141
7142     def test_iterator_twice(self):
7143         class SeqOf(SequenceOf):
7144             schema = Integer()
7145             bounds = (1, float("+inf"))
7146
7147         def gen():
7148             for i in range(10):
7149                 yield Integer(i)
7150         seqof = SeqOf(gen())
7151         self.assertTrue(seqof.ready)
7152         seqof.encode()
7153         self.assertFalse(seqof.ready)
7154         register_class(SeqOf)
7155         pickle_dumps(seqof)
7156
7157     def test_iterator_2pass(self):
7158         class SeqOf(SequenceOf):
7159             schema = Integer()
7160             bounds = (1, float("+inf"))
7161
7162         def gen():
7163             for i in range(10):
7164                 yield Integer(i)
7165         seqof = SeqOf(gen())
7166         self.assertTrue(seqof.ready)
7167         _, state = seqof.encode1st()
7168         self.assertFalse(seqof.ready)
7169         seqof = seqof(gen())
7170         self.assertTrue(seqof.ready)
7171         buf = BytesIO()
7172         seqof.encode2nd(buf.write, iter(state))
7173         self.assertSequenceEqual(
7174             [int(i) for i in seqof.decod(buf.getvalue())],
7175             list(gen()),
7176         )
7177
7178     def test_non_ready_bound_min(self):
7179         class SeqOf(SequenceOf):
7180             schema = Integer()
7181             bounds = (1, float("+inf"))
7182         seqof = SeqOf()
7183         self.assertFalse(seqof.ready)
7184
7185
7186 class TestSetOf(SeqOfMixin, CommonMixin, TestCase):
7187     class SeqOf(SetOf):
7188         schema = "whatever"
7189     base_klass = SeqOf
7190
7191     def _test_symmetric_compare_objs(self, obj1, obj2):
7192         self.assertSetEqual(
7193             set(int(v) for v in obj1),
7194             set(int(v) for v in obj2),
7195         )
7196
7197     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
7198     @given(data_strategy())
7199     def test_sorted(self, d):
7200         values = [OctetString(v) for v in d.draw(lists(binary()))]
7201
7202         class Seq(SetOf):
7203             schema = OctetString()
7204         seq = Seq(values)
7205         seq_encoded = seq.encode()
7206         seq_decoded, _ = seq.decode(seq_encoded)
7207         self.assertSequenceEqual(
7208             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
7209             b"".join(sorted([v.encode() for v in values])),
7210         )
7211
7212     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
7213     @given(data_strategy())
7214     def test_unsorted(self, d):
7215         values = [OctetString(v).encode() for v in d.draw(sets(
7216             binary(min_size=1, max_size=5),
7217             min_size=2,
7218             max_size=5,
7219         ))]
7220         values = d.draw(permutations(values))
7221         assume(values != sorted(values))
7222         encoded = b"".join(values)
7223         seq_encoded = b"".join((
7224             SetOf.tag_default,
7225             len_encode(len(encoded)),
7226             encoded,
7227         ))
7228
7229         class Seq(SetOf):
7230             schema = OctetString()
7231         seq = Seq()
7232         with self.assertRaisesRegex(DecodeError, "unordered SET OF"):
7233             seq.decode(seq_encoded)
7234
7235         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
7236             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
7237             self.assertTrue(seq_decoded.ber_encoded)
7238             self.assertTrue(seq_decoded.bered)
7239             seq_decoded = copy(seq_decoded)
7240             self.assertTrue(seq_decoded.ber_encoded)
7241             self.assertTrue(seq_decoded.bered)
7242             self.assertSequenceEqual(
7243                 [obj.encode() for obj in seq_decoded],
7244                 values,
7245             )
7246
7247
7248 class TestGoMarshalVectors(TestCase):
7249     def runTest(self):
7250         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
7251         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
7252         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
7253         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
7254         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
7255
7256         class Seq(Sequence):
7257             schema = (
7258                 ("erste", Integer()),
7259                 ("zweite", Integer(optional=True))
7260             )
7261         seq = Seq()
7262         seq["erste"] = Integer(64)
7263         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7264         seq["erste"] = Integer(0x123456)
7265         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
7266         seq["erste"] = Integer(64)
7267         seq["zweite"] = Integer(65)
7268         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
7269
7270         class NestedSeq(Sequence):
7271             schema = (
7272                 ("nest", Seq()),
7273             )
7274         seq["erste"] = Integer(127)
7275         seq["zweite"] = None
7276         nested = NestedSeq()
7277         nested["nest"] = seq
7278         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
7279
7280         self.assertSequenceEqual(
7281             OctetString(b"\x01\x02\x03").encode(),
7282             hexdec("0403010203"),
7283         )
7284
7285         class Seq(Sequence):
7286             schema = (
7287                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
7288             )
7289         seq = Seq()
7290         seq["erste"] = Integer(64)
7291         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
7292
7293         class Seq(Sequence):
7294             schema = (
7295                 ("erste", Integer(expl=tag_ctxc(5))),
7296             )
7297         seq = Seq()
7298         seq["erste"] = Integer(64)
7299         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
7300
7301         class Seq(Sequence):
7302             schema = (
7303                 ("erste", Null(
7304                     impl=tag_encode(0, klass=TagClassContext),
7305                     optional=True,
7306                 )),
7307             )
7308         seq = Seq()
7309         seq["erste"] = Null()
7310         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
7311         seq["erste"] = None
7312         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7313
7314         self.assertSequenceEqual(
7315             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
7316             hexdec("170d3730303130313030303030305a"),
7317         )
7318         self.assertSequenceEqual(
7319             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
7320             hexdec("170d3039313131353232353631365a"),
7321         )
7322         self.assertSequenceEqual(
7323             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
7324             hexdec("180f32313030303430353132303130315a"),
7325         )
7326
7327         class Seq(Sequence):
7328             schema = (
7329                 ("erste", GeneralizedTime()),
7330             )
7331         seq = Seq()
7332         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
7333         self.assertSequenceEqual(
7334             seq.encode(),
7335             hexdec("3011180f32303039313131353232353631365a"),
7336         )
7337
7338         self.assertSequenceEqual(
7339             BitString((1, b"\x80")).encode(),
7340             hexdec("03020780"),
7341         )
7342         self.assertSequenceEqual(
7343             BitString((12, b"\x81\xF0")).encode(),
7344             hexdec("03030481f0"),
7345         )
7346
7347         self.assertSequenceEqual(
7348             ObjectIdentifier("1.2.3.4").encode(),
7349             hexdec("06032a0304"),
7350         )
7351         self.assertSequenceEqual(
7352             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
7353             hexdec("06092a864888932d010105"),
7354         )
7355         self.assertSequenceEqual(
7356             ObjectIdentifier("2.100.3").encode(),
7357             hexdec("0603813403"),
7358         )
7359
7360         self.assertSequenceEqual(
7361             PrintableString("test").encode(),
7362             hexdec("130474657374"),
7363         )
7364         self.assertSequenceEqual(
7365             PrintableString("x" * 127).encode(),
7366             hexdec("137F" + "78" * 127),
7367         )
7368         self.assertSequenceEqual(
7369             PrintableString("x" * 128).encode(),
7370             hexdec("138180" + "78" * 128),
7371         )
7372         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
7373
7374         class Seq(Sequence):
7375             schema = (
7376                 ("erste", IA5String()),
7377             )
7378         seq = Seq()
7379         seq["erste"] = IA5String("test")
7380         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
7381
7382         class Seq(Sequence):
7383             schema = (
7384                 ("erste", PrintableString()),
7385             )
7386         seq = Seq()
7387         seq["erste"] = PrintableString("test")
7388         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
7389         # Asterisk is actually not allowable
7390         pyderasn.PRINTABLE_ALLOWABLE_CHARS |= set(b"*")
7391         seq["erste"] = PrintableString("test*")
7392         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
7393         pyderasn.PRINTABLE_ALLOWABLE_CHARS -= set(b"*")
7394
7395         class Seq(Sequence):
7396             schema = (
7397                 ("erste", Any(optional=True)),
7398                 ("zweite", Integer()),
7399             )
7400         seq = Seq()
7401         seq["zweite"] = Integer(64)
7402         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7403
7404         class Seq(SetOf):
7405             schema = Integer()
7406         seq = Seq()
7407         seq.append(Integer(10))
7408         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
7409
7410         class _SeqOf(SequenceOf):
7411             schema = PrintableString()
7412
7413         class SeqOf(SequenceOf):
7414             schema = _SeqOf()
7415         _seqof = _SeqOf()
7416         _seqof.append(PrintableString("1"))
7417         seqof = SeqOf()
7418         seqof.append(_seqof)
7419         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
7420
7421         class Seq(Sequence):
7422             schema = (
7423                 ("erste", Integer(default=1)),
7424             )
7425         seq = Seq()
7426         seq["erste"] = Integer(0)
7427         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
7428         seq["erste"] = Integer(1)
7429         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7430         seq["erste"] = Integer(2)
7431         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
7432
7433
7434 class TestPP(TestCase):
7435     @given(data_strategy())
7436     def test_oid_printing(self, d):
7437         oids = {
7438             str(ObjectIdentifier(k)): v * 2
7439             for k, v in d.draw(dictionaries(
7440                 oid_strategy(),
7441                 text_letters(),
7442                 min_size=1,
7443             )).items()
7444         }
7445         chosen = d.draw(sampled_from(sorted(oids)))
7446         chosen_id = oids[chosen]
7447         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
7448         self.assertNotIn(chosen_id, pp_console_row(pp))
7449         self.assertIn(
7450             chosen_id,
7451             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
7452         )
7453
7454
7455 class TestAutoAddSlots(TestCase):
7456     def runTest(self):
7457         class Inher(Integer):
7458             pass
7459
7460         with self.assertRaises(AttributeError):
7461             inher = Inher()
7462             inher.unexistent = "whatever"
7463
7464
7465 class TestOIDDefines(TestCase):
7466     @given(data_strategy())
7467     def runTest(self, d):
7468         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
7469         value_name_chosen = d.draw(sampled_from(value_names))
7470         oids = [
7471             ObjectIdentifier(oid)
7472             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
7473         ]
7474         oid_chosen = d.draw(sampled_from(oids))
7475         values = d.draw(lists(
7476             integers(),
7477             min_size=len(value_names),
7478             max_size=len(value_names),
7479         ))
7480         for definable_class in (Any, OctetString, BitString):
7481             _schema = [
7482                 ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
7483                     oid: Integer() for oid in oids[:-1]
7484                 }),))),
7485             ]
7486             for i, value_name in enumerate(value_names):
7487                 _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
7488
7489             class Seq(Sequence):
7490                 schema = _schema
7491             seq = Seq()
7492             for value_name, value in zip(value_names, values):
7493                 seq[value_name] = definable_class(Integer(value).encode())
7494             seq["type"] = oid_chosen
7495             seq, _ = Seq().decode(seq.encode())
7496             for value_name in value_names:
7497                 if value_name == value_name_chosen:
7498                     continue
7499                 self.assertIsNone(seq[value_name].defined)
7500             if value_name_chosen in oids[:-1]:
7501                 self.assertIsNotNone(seq[value_name_chosen].defined)
7502                 self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
7503                 self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
7504             repr(seq)
7505             list(seq.pps())
7506             pprint(seq, big_blobs=True, with_decode_path=True)
7507
7508
7509 class TestDefinesByPath(TestCase):
7510     def test_generated(self):
7511         class Seq(Sequence):
7512             schema = (
7513                 ("type", ObjectIdentifier()),
7514                 ("value", OctetString(expl=tag_ctxc(123))),
7515             )
7516
7517         class SeqInner(Sequence):
7518             schema = (
7519                 ("typeInner", ObjectIdentifier()),
7520                 ("valueInner", Any()),
7521             )
7522
7523         class PairValue(SetOf):
7524             schema = Any()
7525
7526         class Pair(Sequence):
7527             schema = (
7528                 ("type", ObjectIdentifier()),
7529                 ("value", PairValue()),
7530             )
7531
7532         class Pairs(SequenceOf):
7533             schema = Pair()
7534
7535         (
7536             type_integered,
7537             type_sequenced,
7538             type_innered,
7539             type_octet_stringed,
7540         ) = [
7541             ObjectIdentifier(oid)
7542             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
7543         ]
7544         seq_integered = Seq()
7545         seq_integered["type"] = type_integered
7546         seq_integered["value"] = OctetString(Integer(123).encode())
7547         seq_integered_raw = seq_integered.encode()
7548
7549         pairs = Pairs()
7550         pairs_input = (
7551             (type_octet_stringed, OctetString(b"whatever")),
7552             (type_integered, Integer(123)),
7553             (type_octet_stringed, OctetString(b"whenever")),
7554             (type_integered, Integer(234)),
7555         )
7556         for t, v in pairs_input:
7557             pairs.append(Pair((
7558                 ("type", t),
7559                 ("value", PairValue((Any(v),))),
7560             )))
7561         seq_inner = SeqInner()
7562         seq_inner["typeInner"] = type_innered
7563         seq_inner["valueInner"] = Any(pairs)
7564         seq_sequenced = Seq()
7565         seq_sequenced["type"] = type_sequenced
7566         seq_sequenced["value"] = OctetString(seq_inner.encode())
7567         seq_sequenced_raw = seq_sequenced.encode()
7568         repr(seq_sequenced)
7569         list(seq_sequenced.pps())
7570         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7571
7572         defines_by_path = []
7573         ctx_copied = deepcopy(ctx_dummy)
7574         seq_integered, _ = Seq().decode(
7575             seq_integered_raw,
7576             ctx=ctx_copied,
7577         )
7578         self.assertDictEqual(ctx_copied, ctx_dummy)
7579         self.assertIsNone(seq_integered["value"].defined)
7580         defines_by_path.append(
7581             (("type",), ((("value",), {
7582                 type_integered: Integer(),
7583                 type_sequenced: SeqInner(),
7584             }),))
7585         )
7586         ctx_copied["defines_by_path"] = defines_by_path
7587         seq_integered, _ = Seq().decode(
7588             seq_integered_raw,
7589             ctx=ctx_copied,
7590         )
7591         del ctx_copied["defines_by_path"]
7592         self.assertDictEqual(ctx_copied, ctx_dummy)
7593         self.assertIsNotNone(seq_integered["value"].defined)
7594         self.assertEqual(seq_integered["value"].defined[0], type_integered)
7595         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
7596         self.assertTrue(seq_integered_raw[
7597             seq_integered["value"].defined[1].offset:
7598         ].startswith(Integer(123).encode()))
7599         repr(seq_integered)
7600         list(seq_integered.pps())
7601         pprint(seq_integered, big_blobs=True, with_decode_path=True)
7602
7603         ctx_copied["defines_by_path"] = defines_by_path
7604         seq_sequenced, _ = Seq().decode(
7605             seq_sequenced_raw,
7606             ctx=ctx_copied,
7607         )
7608         del ctx_copied["defines_by_path"]
7609         self.assertDictEqual(ctx_copied, ctx_dummy)
7610         self.assertIsNotNone(seq_sequenced["value"].defined)
7611         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7612         seq_inner = seq_sequenced["value"].defined[1]
7613         self.assertIsNone(seq_inner["valueInner"].defined)
7614         repr(seq_sequenced)
7615         list(seq_sequenced.pps())
7616         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7617
7618         defines_by_path.append((
7619             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
7620             ((("valueInner",), {type_innered: Pairs()}),),
7621         ))
7622         ctx_copied["defines_by_path"] = defines_by_path
7623         seq_sequenced, _ = Seq().decode(
7624             seq_sequenced_raw,
7625             ctx=ctx_copied,
7626         )
7627         del ctx_copied["defines_by_path"]
7628         self.assertDictEqual(ctx_copied, ctx_dummy)
7629         self.assertIsNotNone(seq_sequenced["value"].defined)
7630         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7631         seq_inner = seq_sequenced["value"].defined[1]
7632         self.assertIsNotNone(seq_inner["valueInner"].defined)
7633         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7634         pairs = seq_inner["valueInner"].defined[1]
7635         for pair in pairs:
7636             self.assertIsNone(pair["value"][0].defined)
7637         repr(seq_sequenced)
7638         list(seq_sequenced.pps())
7639         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7640
7641         defines_by_path.append((
7642             (
7643                 "value",
7644                 DecodePathDefBy(type_sequenced),
7645                 "valueInner",
7646                 DecodePathDefBy(type_innered),
7647                 any,
7648                 "type",
7649             ),
7650             ((("value",), {
7651                 type_integered: Integer(),
7652                 type_octet_stringed: OctetString(),
7653             }),),
7654         ))
7655         ctx_copied["defines_by_path"] = defines_by_path
7656         seq_sequenced, _ = Seq().decode(
7657             seq_sequenced_raw,
7658             ctx=ctx_copied,
7659         )
7660         del ctx_copied["defines_by_path"]
7661         self.assertDictEqual(ctx_copied, ctx_dummy)
7662         self.assertIsNotNone(seq_sequenced["value"].defined)
7663         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7664         seq_inner = seq_sequenced["value"].defined[1]
7665         self.assertIsNotNone(seq_inner["valueInner"].defined)
7666         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7667         pairs_got = seq_inner["valueInner"].defined[1]
7668         for pair_input, pair_got in zip(pairs_input, pairs_got):
7669             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
7670             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
7671         repr(seq_sequenced)
7672         list(seq_sequenced.pps())
7673         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7674
7675     @given(oid_strategy(), integers())
7676     def test_simple(self, oid, tgt):
7677         class Inner(Sequence):
7678             schema = (
7679                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
7680                     ObjectIdentifier(oid): Integer(),
7681                 }),))),
7682             )
7683
7684         class Outer(Sequence):
7685             schema = (
7686                 ("inner", Inner()),
7687                 ("tgt", OctetString()),
7688             )
7689
7690         inner = Inner()
7691         inner["oid"] = ObjectIdentifier(oid)
7692         outer = Outer()
7693         outer["inner"] = inner
7694         outer["tgt"] = OctetString(Integer(tgt).encode())
7695         decoded, _ = Outer().decode(outer.encode())
7696         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
7697
7698     def test_remaining_data(self):
7699         oid = ObjectIdentifier("1.2.3")
7700
7701         class Seq(Sequence):
7702             schema = (
7703                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7704                     oid: Integer(),
7705                 }),))),
7706                 ("tgt", OctetString()),
7707             )
7708
7709         seq = Seq((
7710             ("oid", oid),
7711             ("tgt", OctetString(Integer(123).encode() + b"junk")),
7712         ))
7713         with self.assertRaisesRegex(DecodeError, "remaining data"):
7714             Seq().decode(seq.encode())
7715
7716     def test_remaining_data_seqof(self):
7717         oid = ObjectIdentifier("1.2.3")
7718
7719         class SeqOf(SetOf):
7720             schema = OctetString()
7721
7722         class Seq(Sequence):
7723             schema = (
7724                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7725                     oid: Integer(),
7726                 }),))),
7727                 ("tgt", SeqOf()),
7728             )
7729
7730         seq = Seq((
7731             ("oid", oid),
7732             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
7733         ))
7734         with self.assertRaisesRegex(DecodeError, "remaining data"):
7735             Seq().decode(seq.encode())
7736
7737
7738 class TestAbsDecodePath(TestCase):
7739     @given(
7740         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7741         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7742     )
7743     def test_concat(self, decode_path, rel_path):
7744         dp = abs_decode_path(decode_path, rel_path)
7745         self.assertSequenceEqual(dp, decode_path + rel_path)
7746         repr(dp)
7747
7748     @given(
7749         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7750         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7751     )
7752     def test_abs(self, decode_path, rel_path):
7753         self.assertSequenceEqual(
7754             abs_decode_path(decode_path, ("/",) + rel_path),
7755             rel_path,
7756         )
7757
7758     @given(
7759         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
7760         integers(min_value=1, max_value=3),
7761         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7762     )
7763     def test_dots(self, decode_path, number_of_dots, rel_path):
7764         self.assertSequenceEqual(
7765             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
7766             decode_path[:-number_of_dots] + rel_path,
7767         )
7768
7769
7770 class TestStrictDefaultExistence(TestCase):
7771     @given(data_strategy())
7772     def runTest(self, d):
7773         count = d.draw(integers(min_value=1, max_value=10))
7774         chosen = d.draw(integers(min_value=0, max_value=count - 1))
7775         _schema = [
7776             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
7777             for i in range(count)
7778         ]
7779         for klass in (Sequence, Set):
7780             class Seq(klass):
7781                 schema = _schema
7782             seq = Seq()
7783             for i in range(count):
7784                 seq["int%d" % i] = Integer(123)
7785             raw = seq.encode()
7786             chosen_choice = "int%d" % chosen
7787             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
7788             with self.assertRaisesRegex(DecodeError, "DEFAULT value met"):
7789                 seq.decode(raw)
7790             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
7791             self.assertTrue(decoded.ber_encoded)
7792             self.assertTrue(decoded.bered)
7793             decoded = copy(decoded)
7794             self.assertTrue(decoded.ber_encoded)
7795             self.assertTrue(decoded.bered)
7796             decoded, _ = seq.decode(raw, ctx={"bered": True})
7797             self.assertTrue(decoded.ber_encoded)
7798             self.assertTrue(decoded.bered)
7799             decoded = copy(decoded)
7800             self.assertTrue(decoded.ber_encoded)
7801             self.assertTrue(decoded.bered)
7802
7803
7804 class TestX690PrefixedType(TestCase):
7805     def test_1(self):
7806         self.assertSequenceEqual(
7807             VisibleString("Jones").encode(),
7808             hexdec("1A054A6F6E6573"),
7809         )
7810
7811     def test_2(self):
7812         self.assertSequenceEqual(
7813             VisibleString(
7814                 "Jones",
7815                 impl=tag_encode(3, klass=TagClassApplication),
7816             ).encode(),
7817             hexdec("43054A6F6E6573"),
7818         )
7819
7820     def test_3(self):
7821         self.assertSequenceEqual(
7822             Any(
7823                 VisibleString(
7824                     "Jones",
7825                     impl=tag_encode(3, klass=TagClassApplication),
7826                 ),
7827                 expl=tag_ctxc(2),
7828             ).encode(),
7829             hexdec("A20743054A6F6E6573"),
7830         )
7831
7832     def test_4(self):
7833         self.assertSequenceEqual(
7834             OctetString(
7835                 VisibleString(
7836                     "Jones",
7837                     impl=tag_encode(3, klass=TagClassApplication),
7838                 ).encode(),
7839                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
7840             ).encode(),
7841             hexdec("670743054A6F6E6573"),
7842         )
7843
7844     def test_5(self):
7845         self.assertSequenceEqual(
7846             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
7847             hexdec("82054A6F6E6573"),
7848         )
7849
7850
7851 class TestExplOOB(TestCase):
7852     def runTest(self):
7853         expl = tag_ctxc(123)
7854         raw = Integer(123).encode() + Integer(234).encode()
7855         raw = b"".join((expl, len_encode(len(raw)), raw))
7856         with self.assertRaisesRegex(DecodeError, "explicit tag out-of-bound"):
7857             Integer(expl=expl).decode(raw)
7858         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
7859
7860
7861 class TestPickleDifferentVersion(TestCase):
7862     def runTest(self):
7863         pickled = pickle_dumps(Integer(123), pickle_proto)
7864         import pyderasn
7865         version_orig = pyderasn.__version__
7866         pyderasn.__version__ += "different"
7867         with self.assertRaisesRegex(ValueError, "different PyDERASN version"):
7868             pickle_loads(pickled)
7869         pyderasn.__version__ = version_orig
7870         pickle_loads(pickled)
7871
7872
7873 class TestCERSetOrdering(TestCase):
7874     def test_vectors(self):
7875         """Taken from X.690-201508
7876         """
7877         class B(Choice):
7878             schema = (
7879                 ("c", Integer(impl=tag_ctxp(2))),
7880                 ("d", Integer(impl=tag_ctxp(4))),
7881             )
7882
7883         class F(Choice):
7884             schema = (
7885                 ("g", Integer(impl=tag_ctxp(5))),
7886                 ("h", Integer(impl=tag_ctxp(6))),
7887             )
7888
7889         class I(Choice):
7890             schema = (
7891                 ("j", Integer(impl=tag_ctxp(0))),
7892             )
7893
7894         class E(Choice):
7895             schema = (
7896                 ("f", F()),
7897                 ("i", I()),
7898             )
7899
7900         class A(Set):
7901             schema = (
7902                 ("a", Integer(impl=tag_ctxp(3))),
7903                 ("b", B(expl=tag_ctxc(1))),
7904                 ("e", E()),
7905             )
7906
7907         a = A((
7908             ("a", Integer(123)),
7909             ("b", B(("d", Integer(234)))),
7910             ("e", E(("f", F(("g", Integer(345)))))),
7911         ))
7912         order = sorted(a._values_for_encoding(), key=attrgetter("tag_order_cer"))
7913         self.assertSequenceEqual(
7914             [i.__class__.__name__ for i in order],
7915             ("E", "B", "Integer"),
7916         )