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