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