]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Check that explicit tag is not out-of-bounds
[pyderasn.git] / tests / test_pyderasn.py
1 # coding: utf-8
2 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
3 # Copyright (C) 2017-2018 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, either version 3 of the
8 # License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 from datetime import datetime
20 from string import ascii_letters
21 from string import digits
22 from string import printable
23 from string import whitespace
24 from unittest import TestCase
25
26 from hypothesis import assume
27 from hypothesis import given
28 from hypothesis import settings
29 from hypothesis.strategies import binary
30 from hypothesis.strategies import booleans
31 from hypothesis.strategies import composite
32 from hypothesis.strategies import data as data_strategy
33 from hypothesis.strategies import datetimes
34 from hypothesis.strategies import dictionaries
35 from hypothesis.strategies import integers
36 from hypothesis.strategies import just
37 from hypothesis.strategies import lists
38 from hypothesis.strategies import none
39 from hypothesis.strategies import one_of
40 from hypothesis.strategies import permutations
41 from hypothesis.strategies import sampled_from
42 from hypothesis.strategies import sets
43 from hypothesis.strategies import text
44 from hypothesis.strategies import tuples
45 from six import assertRaisesRegex
46 from six import binary_type
47 from six import byte2int
48 from six import indexbytes
49 from six import int2byte
50 from six import iterbytes
51 from six import PY2
52 from six import text_type
53 from six import unichr as six_unichr
54
55 from pyderasn import _pp
56 from pyderasn import abs_decode_path
57 from pyderasn import Any
58 from pyderasn import BitString
59 from pyderasn import BMPString
60 from pyderasn import Boolean
61 from pyderasn import BoundsError
62 from pyderasn import Choice
63 from pyderasn import DecodeError
64 from pyderasn import DecodePathDefBy
65 from pyderasn import Enumerated
66 from pyderasn import EOC
67 from pyderasn import EOC_LEN
68 from pyderasn import GeneralizedTime
69 from pyderasn import GeneralString
70 from pyderasn import GraphicString
71 from pyderasn import hexdec
72 from pyderasn import hexenc
73 from pyderasn import IA5String
74 from pyderasn import Integer
75 from pyderasn import InvalidLength
76 from pyderasn import InvalidOID
77 from pyderasn import InvalidValueType
78 from pyderasn import len_decode
79 from pyderasn import len_encode
80 from pyderasn import LEN_YYMMDDHHMMSSZ
81 from pyderasn import LEN_YYYYMMDDHHMMSSDMZ
82 from pyderasn import LEN_YYYYMMDDHHMMSSZ
83 from pyderasn import LENINDEF
84 from pyderasn import NotEnoughData
85 from pyderasn import Null
86 from pyderasn import NumericString
87 from pyderasn import ObjectIdentifier
88 from pyderasn import ObjNotReady
89 from pyderasn import ObjUnknown
90 from pyderasn import OctetString
91 from pyderasn import pp_console_row
92 from pyderasn import pprint
93 from pyderasn import PrintableString
94 from pyderasn import Sequence
95 from pyderasn import SequenceOf
96 from pyderasn import Set
97 from pyderasn import SetOf
98 from pyderasn import tag_ctxc
99 from pyderasn import tag_ctxp
100 from pyderasn import tag_decode
101 from pyderasn import tag_encode
102 from pyderasn import tag_strip
103 from pyderasn import TagClassApplication
104 from pyderasn import TagClassContext
105 from pyderasn import TagClassPrivate
106 from pyderasn import TagClassUniversal
107 from pyderasn import TagFormConstructed
108 from pyderasn import TagFormPrimitive
109 from pyderasn import TagMismatch
110 from pyderasn import TeletexString
111 from pyderasn import UniversalString
112 from pyderasn import UTCTime
113 from pyderasn import UTF8String
114 from pyderasn import VideotexString
115 from pyderasn import VisibleString
116
117
118 settings.register_profile("local", settings(
119     deadline=5000,
120 ))
121 settings.load_profile("local")
122 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
123
124 tag_classes = sampled_from((
125     TagClassApplication,
126     TagClassContext,
127     TagClassPrivate,
128     TagClassUniversal,
129 ))
130 tag_forms = sampled_from((TagFormConstructed, TagFormPrimitive))
131 decode_path_strat = lists(integers(), max_size=3).map(
132     lambda decode_path: tuple(str(dp) for dp in decode_path)
133 )
134
135
136 class TestHex(TestCase):
137     @given(binary())
138     def test_symmetric(self, data):
139         self.assertEqual(hexdec(hexenc(data)), data)
140
141
142 class TestTagCoder(TestCase):
143     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
144     @given(
145         tag_classes,
146         tag_forms,
147         integers(min_value=0, max_value=30),
148         binary(max_size=5),
149     )
150     def test_short(self, klass, form, num, junk):
151         raw = tag_encode(klass=klass, form=form, num=num)
152         self.assertEqual(tag_decode(raw), (klass, form, num))
153         self.assertEqual(len(raw), 1)
154         self.assertEqual(
155             byte2int(tag_encode(klass=klass, form=form, num=0)),
156             byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
157         )
158         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
159         self.assertSequenceEqual(stripped.tobytes(), raw)
160         self.assertEqual(tlen, len(raw))
161         self.assertSequenceEqual(tail, junk)
162
163     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
164     @given(
165         tag_classes,
166         tag_forms,
167         integers(min_value=31),
168         binary(max_size=5),
169     )
170     def test_long(self, klass, form, num, junk):
171         raw = tag_encode(klass=klass, form=form, num=num)
172         self.assertEqual(tag_decode(raw), (klass, form, num))
173         self.assertGreater(len(raw), 1)
174         self.assertEqual(
175             byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
176             byte2int(raw[:1]),
177         )
178         self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
179         self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
180         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
181         self.assertSequenceEqual(stripped.tobytes(), raw)
182         self.assertEqual(tlen, len(raw))
183         self.assertSequenceEqual(tail, junk)
184
185     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
186     @given(integers(min_value=31))
187     def test_unfinished_tag(self, num):
188         raw = bytearray(tag_encode(num=num))
189         for i in range(1, len(raw)):
190             raw[i] |= 0x80
191         with assertRaisesRegex(self, DecodeError, "unfinished tag"):
192             tag_strip(bytes(raw))
193
194     def test_go_vectors_valid(self):
195         for data, (eklass, etag, elen, eform) in (
196                 (b"\x80\x01", (TagClassContext, 0, 1, TagFormPrimitive)),
197                 (b"\xa0\x01", (TagClassContext, 0, 1, TagFormConstructed)),
198                 (b"\x02\x00", (TagClassUniversal, 2, 0, TagFormPrimitive)),
199                 (b"\xfe\x00", (TagClassPrivate, 30, 0, TagFormConstructed)),
200                 (b"\x1f\x1f\x00", (TagClassUniversal, 31, 0, TagFormPrimitive)),
201                 (b"\x1f\x81\x00\x00", (TagClassUniversal, 128, 0, TagFormPrimitive)),
202                 (b"\x1f\x81\x80\x01\x00", (TagClassUniversal, 0x4001, 0, TagFormPrimitive)),
203                 (b"\x00\x81\x80", (TagClassUniversal, 0, 128, TagFormPrimitive)),
204                 (b"\x00\x82\x01\x00", (TagClassUniversal, 0, 256, TagFormPrimitive)),
205                 (b"\xa0\x84\x7f\xff\xff\xff", (TagClassContext, 0, 0x7fffffff, TagFormConstructed)),
206         ):
207             tag, _, len_encoded = tag_strip(memoryview(data))
208             klass, form, num = tag_decode(tag)
209             _len, _, tail = len_decode(len_encoded)
210             self.assertSequenceEqual(tail, b"")
211             self.assertEqual(klass, eklass)
212             self.assertEqual(num, etag)
213             self.assertEqual(_len, elen)
214             self.assertEqual(form, eform)
215
216     def test_go_vectors_invalid(self):
217         for data in (
218                 b"\x00\x83\x01\x00",
219                 b"\x1f\x85",
220                 b"\x30\x80",
221                 b"\xa0\x82\x00\xff",
222                 b"\xa0\x81\x7f",
223         ):
224             with self.assertRaises(DecodeError):
225                 _, _, len_encoded = tag_strip(memoryview(data))
226                 len_decode(len_encoded)
227
228     @given(
229         integers(min_value=0, max_value=127),
230         integers(min_value=0, max_value=2),
231     )
232     def test_long_instead_of_short(self, l, dummy_num):
233         octets = (b"\x00" * dummy_num) + int2byte(l)
234         octets = int2byte((dummy_num + 1) | 0x80) + octets
235         with self.assertRaises(DecodeError):
236             len_decode(octets)
237
238
239 class TestLenCoder(TestCase):
240     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
241     @given(
242         integers(min_value=0, max_value=127),
243         binary(max_size=5),
244     )
245     def test_short(self, l, junk):
246         raw = len_encode(l) + junk
247         decoded, llen, tail = len_decode(memoryview(raw))
248         self.assertEqual(decoded, l)
249         self.assertEqual(llen, 1)
250         self.assertEqual(len(raw), 1 + len(junk))
251         self.assertEqual(tail.tobytes(), junk)
252
253     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
254     @given(
255         integers(min_value=128),
256         binary(max_size=5),
257     )
258     def test_long(self, l, junk):
259         raw = len_encode(l) + junk
260         decoded, llen, tail = len_decode(memoryview(raw))
261         self.assertEqual(decoded, l)
262         self.assertEqual((llen - 1) | 0x80, byte2int(raw))
263         self.assertEqual(llen, len(raw) - len(junk))
264         self.assertNotEqual(indexbytes(raw, 1), 0)
265         self.assertSequenceEqual(tail.tobytes(), junk)
266
267     def test_empty(self):
268         with self.assertRaises(NotEnoughData):
269             len_decode(b"")
270
271     @given(integers(min_value=128))
272     def test_stripped(self, _len):
273         with self.assertRaises(NotEnoughData):
274             len_decode(len_encode(_len)[:-1])
275
276
277 text_printable = text(alphabet=printable, min_size=1)
278
279
280 @composite
281 def text_letters(draw):
282     result = draw(text(alphabet=ascii_letters, min_size=1))
283     if PY2:
284         result = result.encode("ascii")
285     return result
286
287
288 class CommonMixin(object):
289     def test_tag_default(self):
290         obj = self.base_klass()
291         self.assertEqual(obj.tag, obj.tag_default)
292
293     def test_simultaneous_impl_expl(self):
294         with self.assertRaises(ValueError):
295             self.base_klass(impl=b"whatever", expl=b"whenever")
296
297     @given(binary(min_size=1), integers(), integers(), integers())
298     def test_decoded(self, impl, offset, llen, vlen):
299         obj = self.base_klass(impl=impl, _decoded=(offset, llen, vlen))
300         self.assertEqual(obj.offset, offset)
301         self.assertEqual(obj.llen, llen)
302         self.assertEqual(obj.vlen, vlen)
303         self.assertEqual(obj.tlen, len(impl))
304         self.assertEqual(obj.tlvlen, obj.tlen + obj.llen + obj.vlen)
305
306     @given(binary(min_size=1))
307     def test_impl_inherited(self, impl_tag):
308         class Inherited(self.base_klass):
309             impl = impl_tag
310         obj = Inherited()
311         self.assertSequenceEqual(obj.impl, impl_tag)
312         self.assertFalse(obj.expled)
313
314     @given(binary())
315     def test_expl_inherited(self, expl_tag):
316         class Inherited(self.base_klass):
317             expl = expl_tag
318         obj = Inherited()
319         self.assertSequenceEqual(obj.expl, expl_tag)
320         self.assertTrue(obj.expled)
321
322     def assert_copied_basic_fields(self, obj, obj_copied):
323         self.assertEqual(obj, obj_copied)
324         self.assertSequenceEqual(obj.tag, obj_copied.tag)
325         self.assertEqual(obj.expl_tag, obj_copied.expl_tag)
326         self.assertEqual(obj.default, obj_copied.default)
327         self.assertEqual(obj.optional, obj_copied.optional)
328         self.assertEqual(obj.offset, obj_copied.offset)
329         self.assertEqual(obj.llen, obj_copied.llen)
330         self.assertEqual(obj.vlen, obj_copied.vlen)
331
332
333 @composite
334 def boolean_values_strategy(draw, do_expl=False):
335     value = draw(one_of(none(), booleans()))
336     impl = None
337     expl = None
338     if do_expl:
339         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
340     else:
341         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
342     default = draw(one_of(none(), booleans()))
343     optional = draw(one_of(none(), booleans()))
344     _decoded = (
345         draw(integers(min_value=0)),
346         draw(integers(min_value=0)),
347         draw(integers(min_value=0)),
348     )
349     return (value, impl, expl, default, optional, _decoded)
350
351
352 class BooleanInherited(Boolean):
353     pass
354
355
356 class TestBoolean(CommonMixin, TestCase):
357     base_klass = Boolean
358
359     def test_invalid_value_type(self):
360         with self.assertRaises(InvalidValueType) as err:
361             Boolean(123)
362         repr(err.exception)
363
364     @given(booleans())
365     def test_optional(self, optional):
366         obj = Boolean(default=Boolean(False), optional=optional)
367         self.assertTrue(obj.optional)
368
369     @given(booleans())
370     def test_ready(self, value):
371         obj = Boolean()
372         self.assertFalse(obj.ready)
373         repr(obj)
374         pprint(obj)
375         with self.assertRaises(ObjNotReady) as err:
376             obj.encode()
377         repr(err.exception)
378         obj = Boolean(value)
379         self.assertTrue(obj.ready)
380         repr(obj)
381         pprint(obj)
382
383     @given(booleans(), booleans(), binary(), binary())
384     def test_comparison(self, value1, value2, tag1, tag2):
385         for klass in (Boolean, BooleanInherited):
386             obj1 = klass(value1)
387             obj2 = klass(value2)
388             self.assertEqual(obj1 == obj2, value1 == value2)
389             self.assertEqual(obj1 != obj2, value1 != value2)
390             self.assertEqual(obj1 == bool(obj2), value1 == value2)
391             obj1 = klass(value1, impl=tag1)
392             obj2 = klass(value1, impl=tag2)
393             self.assertEqual(obj1 == obj2, tag1 == tag2)
394             self.assertEqual(obj1 != obj2, tag1 != tag2)
395
396     @given(data_strategy())
397     def test_call(self, d):
398         for klass in (Boolean, BooleanInherited):
399             (
400                 value_initial,
401                 impl_initial,
402                 expl_initial,
403                 default_initial,
404                 optional_initial,
405                 _decoded_initial,
406             ) = d.draw(boolean_values_strategy())
407             obj_initial = klass(
408                 value_initial,
409                 impl_initial,
410                 expl_initial,
411                 default_initial,
412                 optional_initial or False,
413                 _decoded_initial,
414             )
415             (
416                 value,
417                 impl,
418                 expl,
419                 default,
420                 optional,
421                 _decoded,
422             ) = d.draw(boolean_values_strategy(do_expl=impl_initial is None))
423             obj = obj_initial(value, impl, expl, default, optional)
424             if obj.ready:
425                 value_expected = default if value is None else value
426                 value_expected = (
427                     default_initial if value_expected is None
428                     else value_expected
429                 )
430                 self.assertEqual(obj, value_expected)
431             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
432             self.assertEqual(obj.expl_tag, expl or expl_initial)
433             self.assertEqual(
434                 obj.default,
435                 default_initial if default is None else default,
436             )
437             if obj.default is None:
438                 optional = optional_initial if optional is None else optional
439                 optional = False if optional is None else optional
440             else:
441                 optional = True
442             self.assertEqual(obj.optional, optional)
443
444     @given(boolean_values_strategy())
445     def test_copy(self, values):
446         for klass in (Boolean, BooleanInherited):
447             obj = klass(*values)
448             obj_copied = obj.copy()
449             self.assert_copied_basic_fields(obj, obj_copied)
450
451     @given(
452         booleans(),
453         integers(min_value=1).map(tag_encode),
454     )
455     def test_stripped(self, value, tag_impl):
456         obj = Boolean(value, impl=tag_impl)
457         with self.assertRaises(NotEnoughData):
458             obj.decode(obj.encode()[:-1])
459
460     @given(
461         booleans(),
462         integers(min_value=1).map(tag_ctxc),
463     )
464     def test_stripped_expl(self, value, tag_expl):
465         obj = Boolean(value, expl=tag_expl)
466         with self.assertRaises(NotEnoughData):
467             obj.decode(obj.encode()[:-1])
468
469     @given(
470         integers(min_value=31),
471         integers(min_value=0),
472         decode_path_strat,
473     )
474     def test_bad_tag(self, tag, offset, decode_path):
475         with self.assertRaises(DecodeError) as err:
476             Boolean().decode(
477                 tag_encode(tag)[:-1],
478                 offset=offset,
479                 decode_path=decode_path,
480             )
481         repr(err.exception)
482         self.assertEqual(err.exception.offset, offset)
483         self.assertEqual(err.exception.decode_path, decode_path)
484
485     @given(
486         integers(min_value=31),
487         integers(min_value=0),
488         decode_path_strat,
489     )
490     def test_bad_expl_tag(self, tag, offset, decode_path):
491         with self.assertRaises(DecodeError) as err:
492             Boolean(expl=Boolean.tag_default).decode(
493                 tag_encode(tag)[:-1],
494                 offset=offset,
495                 decode_path=decode_path,
496             )
497         repr(err.exception)
498         self.assertEqual(err.exception.offset, offset)
499         self.assertEqual(err.exception.decode_path, decode_path)
500
501     @given(
502         integers(min_value=128),
503         integers(min_value=0),
504         decode_path_strat,
505     )
506     def test_bad_len(self, l, offset, decode_path):
507         with self.assertRaises(DecodeError) as err:
508             Boolean().decode(
509                 Boolean.tag_default + len_encode(l)[:-1],
510                 offset=offset,
511                 decode_path=decode_path,
512             )
513         repr(err.exception)
514         self.assertEqual(err.exception.offset, offset)
515         self.assertEqual(err.exception.decode_path, decode_path)
516
517     @given(
518         integers(min_value=128),
519         integers(min_value=0),
520         decode_path_strat,
521     )
522     def test_bad_expl_len(self, l, offset, decode_path):
523         with self.assertRaises(DecodeError) as err:
524             Boolean(expl=Boolean.tag_default).decode(
525                 Boolean.tag_default + len_encode(l)[:-1],
526                 offset=offset,
527                 decode_path=decode_path,
528             )
529         repr(err.exception)
530         self.assertEqual(err.exception.offset, offset)
531         self.assertEqual(err.exception.decode_path, decode_path)
532
533     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
534     @given(
535         boolean_values_strategy(),
536         booleans(),
537         integers(min_value=1).map(tag_ctxc),
538         integers(min_value=0),
539         binary(max_size=5),
540     )
541     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
542         for klass in (Boolean, BooleanInherited):
543             _, _, _, default, optional, _decoded = values
544             obj = klass(
545                 value=value,
546                 default=default,
547                 optional=optional,
548                 _decoded=_decoded,
549             )
550             repr(obj)
551             pprint(obj)
552             self.assertFalse(obj.expled)
553             obj_encoded = obj.encode()
554             obj_expled = obj(value, expl=tag_expl)
555             self.assertTrue(obj_expled.expled)
556             repr(obj_expled)
557             pprint(obj_expled)
558             obj_expled_encoded = obj_expled.encode()
559             obj_decoded, tail = obj_expled.decode(
560                 obj_expled_encoded + tail_junk,
561                 offset=offset,
562             )
563             repr(obj_decoded)
564             pprint(obj_decoded)
565             self.assertEqual(tail, tail_junk)
566             self.assertEqual(obj_decoded, obj_expled)
567             self.assertNotEqual(obj_decoded, obj)
568             self.assertEqual(bool(obj_decoded), bool(obj_expled))
569             self.assertEqual(bool(obj_decoded), bool(obj))
570             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
571             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
572             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
573             self.assertEqual(
574                 obj_decoded.expl_llen,
575                 len(len_encode(len(obj_encoded))),
576             )
577             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
578             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
579             self.assertEqual(
580                 obj_decoded.offset,
581                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
582             )
583             self.assertEqual(obj_decoded.expl_offset, offset)
584
585     @given(integers(min_value=2))
586     def test_invalid_len(self, l):
587         with self.assertRaises(InvalidLength):
588             Boolean().decode(b"".join((
589                 Boolean.tag_default,
590                 len_encode(l),
591                 b"\x00" * l,
592             )))
593
594     @given(integers(min_value=0 + 1, max_value=255 - 1))
595     def test_ber_value(self, value):
596         with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
597             Boolean().decode(b"".join((
598                 Boolean.tag_default,
599                 len_encode(1),
600                 int2byte(value),
601             )))
602         obj, _ = Boolean().decode(
603             b"".join((
604                 Boolean.tag_default,
605                 len_encode(1),
606                 int2byte(value),
607             )),
608             ctx={"bered": True},
609         )
610         self.assertTrue(bool(obj))
611         self.assertTrue(obj.bered)
612         self.assertFalse(obj.lenindef)
613
614     @given(
615         integers(min_value=1).map(tag_ctxc),
616         binary().filter(lambda x: not x.startswith(EOC)),
617     )
618     def test_ber_expl_no_eoc(self, expl, junk):
619         encoded = expl + LENINDEF + Boolean(False).encode()
620         with assertRaisesRegex(self, DecodeError, "no EOC"):
621             Boolean(expl=expl).decode(encoded + junk, ctx={"bered": True})
622         obj, tail = Boolean(expl=expl).decode(
623             encoded + EOC + junk,
624             ctx={"bered": True},
625         )
626         self.assertTrue(obj.expl_lenindef)
627         self.assertSequenceEqual(tail, junk)
628
629     @given(
630         integers(min_value=1).map(tag_ctxc),
631         lists(
632             booleans(),
633             min_size=1,
634             max_size=5,
635         ),
636     )
637     def test_ber_expl(self, expl, values):
638         encoded = b""
639         for value in values:
640             encoded += (
641                 expl +
642                 LENINDEF +
643                 Boolean(value).encode() +
644                 EOC
645             )
646         encoded = SequenceOf.tag_default + len_encode(len(encoded)) + encoded
647
648         class SeqOf(SequenceOf):
649             schema = Boolean(expl=expl)
650         seqof, tail = SeqOf().decode(encoded, ctx={"bered": True})
651         self.assertSequenceEqual(tail, b"")
652         self.assertSequenceEqual([bool(v) for v in seqof], values)
653         self.assertSetEqual(
654             set(
655                 (
656                     v.tlvlen,
657                     v.expl_tlvlen,
658                     v.expl_tlen,
659                     v.expl_llen,
660                     v.bered,
661                     v.lenindef,
662                     v.expl_lenindef,
663                 ) for v in seqof
664             ),
665             set(((
666                 3 + EOC_LEN,
667                 len(expl) + 1 + 3 + EOC_LEN,
668                 len(expl),
669                 1,
670                 False,
671                 False,
672                 True,
673             ),)),
674         )
675
676
677 @composite
678 def integer_values_strategy(draw, do_expl=False):
679     bound_min, value, default, bound_max = sorted(draw(sets(
680         integers(),
681         min_size=4,
682         max_size=4,
683     )))
684     if draw(booleans()):
685         value = None
686     _specs = None
687     if draw(booleans()):
688         _specs = draw(sets(text_letters()))
689         values = draw(sets(
690             integers(),
691             min_size=len(_specs),
692             max_size=len(_specs),
693         ))
694         _specs = list(zip(_specs, values))
695     bounds = None
696     if draw(booleans()):
697         bounds = (bound_min, bound_max)
698     impl = None
699     expl = None
700     if do_expl:
701         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
702     else:
703         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
704     if draw(booleans()):
705         default = None
706     optional = draw(one_of(none(), booleans()))
707     _decoded = (
708         draw(integers(min_value=0)),
709         draw(integers(min_value=0)),
710         draw(integers(min_value=0)),
711     )
712     return (value, bounds, impl, expl, default, optional, _specs, _decoded)
713
714
715 class IntegerInherited(Integer):
716     pass
717
718
719 class TestInteger(CommonMixin, TestCase):
720     base_klass = Integer
721
722     def test_invalid_value_type(self):
723         with self.assertRaises(InvalidValueType) as err:
724             Integer(12.3)
725         repr(err.exception)
726
727     @given(sets(text_letters(), min_size=2))
728     def test_unknown_name(self, names_input):
729         missing = names_input.pop()
730
731         class Int(Integer):
732             schema = [(n, 123) for n in names_input]
733         with self.assertRaises(ObjUnknown) as err:
734             Int(missing)
735         repr(err.exception)
736
737     @given(sets(text_letters(), min_size=2))
738     def test_known_name(self, names_input):
739         class Int(Integer):
740             schema = [(n, 123) for n in names_input]
741         Int(names_input.pop())
742
743     @given(booleans())
744     def test_optional(self, optional):
745         obj = Integer(default=Integer(0), optional=optional)
746         self.assertTrue(obj.optional)
747
748     @given(integers())
749     def test_ready(self, value):
750         obj = Integer()
751         self.assertFalse(obj.ready)
752         repr(obj)
753         pprint(obj)
754         with self.assertRaises(ObjNotReady) as err:
755             obj.encode()
756         repr(err.exception)
757         obj = Integer(value)
758         self.assertTrue(obj.ready)
759         repr(obj)
760         pprint(obj)
761         hash(obj)
762
763     @given(integers(), integers(), binary(), binary())
764     def test_comparison(self, value1, value2, tag1, tag2):
765         for klass in (Integer, IntegerInherited):
766             obj1 = klass(value1)
767             obj2 = klass(value2)
768             self.assertEqual(obj1 == obj2, value1 == value2)
769             self.assertEqual(obj1 != obj2, value1 != value2)
770             self.assertEqual(obj1 == int(obj2), value1 == value2)
771             obj1 = klass(value1, impl=tag1)
772             obj2 = klass(value1, impl=tag2)
773             self.assertEqual(obj1 == obj2, tag1 == tag2)
774             self.assertEqual(obj1 != obj2, tag1 != tag2)
775
776     @given(lists(integers()))
777     def test_sorted_works(self, values):
778         self.assertSequenceEqual(
779             [int(v) for v in sorted(Integer(v) for v in values)],
780             sorted(values),
781         )
782
783     @given(data_strategy())
784     def test_named(self, d):
785         names_input = list(d.draw(sets(text_letters(), min_size=1)))
786         values_input = list(d.draw(sets(
787             integers(),
788             min_size=len(names_input),
789             max_size=len(names_input),
790         )))
791         chosen_name = d.draw(sampled_from(names_input))
792         names_input = dict(zip(names_input, values_input))
793
794         class Int(Integer):
795             schema = names_input
796         _int = Int(chosen_name)
797         self.assertEqual(_int.named, chosen_name)
798         self.assertEqual(int(_int), names_input[chosen_name])
799
800     @given(integers(), integers(min_value=0), integers(min_value=0))
801     def test_bounds_satisfied(self, bound_min, bound_delta, value_delta):
802         value = bound_min + value_delta
803         bound_max = value + bound_delta
804         Integer(value=value, bounds=(bound_min, bound_max))
805
806     @given(sets(integers(), min_size=3, max_size=3))
807     def test_bounds_unsatisfied(self, values):
808         values = sorted(values)
809         with self.assertRaises(BoundsError) as err:
810             Integer(value=values[0], bounds=(values[1], values[2]))
811         repr(err.exception)
812         with assertRaisesRegex(self, DecodeError, "bounds") as err:
813             Integer(bounds=(values[1], values[2])).decode(
814                 Integer(values[0]).encode()
815             )
816         repr(err.exception)
817         with self.assertRaises(BoundsError) as err:
818             Integer(value=values[2], bounds=(values[0], values[1]))
819         repr(err.exception)
820         with assertRaisesRegex(self, DecodeError, "bounds") as err:
821             Integer(bounds=(values[0], values[1])).decode(
822                 Integer(values[2]).encode()
823             )
824         repr(err.exception)
825
826     @given(data_strategy())
827     def test_call(self, d):
828         for klass in (Integer, IntegerInherited):
829             (
830                 value_initial,
831                 bounds_initial,
832                 impl_initial,
833                 expl_initial,
834                 default_initial,
835                 optional_initial,
836                 _specs_initial,
837                 _decoded_initial,
838             ) = d.draw(integer_values_strategy())
839             obj_initial = klass(
840                 value_initial,
841                 bounds_initial,
842                 impl_initial,
843                 expl_initial,
844                 default_initial,
845                 optional_initial or False,
846                 _specs_initial,
847                 _decoded_initial,
848             )
849             (
850                 value,
851                 bounds,
852                 impl,
853                 expl,
854                 default,
855                 optional,
856                 _,
857                 _decoded,
858             ) = d.draw(integer_values_strategy(do_expl=impl_initial is None))
859             if (default is None) and (obj_initial.default is not None):
860                 bounds = None
861             if (
862                     (bounds is None) and
863                     (value is not None) and
864                     (bounds_initial is not None) and
865                     not (bounds_initial[0] <= value <= bounds_initial[1])
866             ):
867                 value = None
868             if (
869                     (bounds is None) and
870                     (default is not None) and
871                     (bounds_initial is not None) and
872                     not (bounds_initial[0] <= default <= bounds_initial[1])
873             ):
874                 default = None
875             obj = obj_initial(value, bounds, impl, expl, default, optional)
876             if obj.ready:
877                 value_expected = default if value is None else value
878                 value_expected = (
879                     default_initial if value_expected is None
880                     else value_expected
881                 )
882                 self.assertEqual(obj, value_expected)
883             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
884             self.assertEqual(obj.expl_tag, expl or expl_initial)
885             self.assertEqual(
886                 obj.default,
887                 default_initial if default is None else default,
888             )
889             if obj.default is None:
890                 optional = optional_initial if optional is None else optional
891                 optional = False if optional is None else optional
892             else:
893                 optional = True
894             self.assertEqual(obj.optional, optional)
895             self.assertEqual(
896                 (obj._bound_min, obj._bound_max),
897                 bounds or bounds_initial or (float("-inf"), float("+inf")),
898             )
899             self.assertEqual(
900                 obj.specs,
901                 {} if _specs_initial is None else dict(_specs_initial),
902             )
903
904     @given(integer_values_strategy())
905     def test_copy(self, values):
906         for klass in (Integer, IntegerInherited):
907             obj = klass(*values)
908             obj_copied = obj.copy()
909             self.assert_copied_basic_fields(obj, obj_copied)
910             self.assertEqual(obj.specs, obj_copied.specs)
911             self.assertEqual(obj._bound_min, obj_copied._bound_min)
912             self.assertEqual(obj._bound_max, obj_copied._bound_max)
913             self.assertEqual(obj._value, obj_copied._value)
914
915     @given(
916         integers(),
917         integers(min_value=1).map(tag_encode),
918     )
919     def test_stripped(self, value, tag_impl):
920         obj = Integer(value, impl=tag_impl)
921         with self.assertRaises(NotEnoughData):
922             obj.decode(obj.encode()[:-1])
923
924     @given(
925         integers(),
926         integers(min_value=1).map(tag_ctxc),
927     )
928     def test_stripped_expl(self, value, tag_expl):
929         obj = Integer(value, expl=tag_expl)
930         with self.assertRaises(NotEnoughData):
931             obj.decode(obj.encode()[:-1])
932
933     def test_zero_len(self):
934         with self.assertRaises(NotEnoughData):
935             Integer().decode(b"".join((
936                 Integer.tag_default,
937                 len_encode(0),
938             )))
939
940     @given(
941         integers(min_value=31),
942         integers(min_value=0),
943         decode_path_strat,
944     )
945     def test_bad_tag(self, tag, offset, decode_path):
946         with self.assertRaises(DecodeError) as err:
947             Integer().decode(
948                 tag_encode(tag)[:-1],
949                 offset=offset,
950                 decode_path=decode_path,
951             )
952         repr(err.exception)
953         self.assertEqual(err.exception.offset, offset)
954         self.assertEqual(err.exception.decode_path, decode_path)
955
956     @given(
957         integers(min_value=128),
958         integers(min_value=0),
959         decode_path_strat,
960     )
961     def test_bad_len(self, l, offset, decode_path):
962         with self.assertRaises(DecodeError) as err:
963             Integer().decode(
964                 Integer.tag_default + len_encode(l)[:-1],
965                 offset=offset,
966                 decode_path=decode_path,
967             )
968         repr(err.exception)
969         self.assertEqual(err.exception.offset, offset)
970         self.assertEqual(err.exception.decode_path, decode_path)
971
972     @given(
973         sets(integers(), min_size=2, max_size=2),
974         integers(min_value=0),
975         decode_path_strat,
976     )
977     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
978         value, bound_min = list(sorted(ints))
979
980         class Int(Integer):
981             bounds = (bound_min, bound_min)
982         with self.assertRaises(DecodeError) as err:
983             Int().decode(
984                 Integer(value).encode(),
985                 offset=offset,
986                 decode_path=decode_path,
987             )
988         repr(err.exception)
989         self.assertEqual(err.exception.offset, offset)
990         self.assertEqual(err.exception.decode_path, decode_path)
991
992     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
993     @given(
994         integer_values_strategy(),
995         integers(),
996         integers(min_value=1).map(tag_ctxc),
997         integers(min_value=0),
998         binary(max_size=5),
999     )
1000     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
1001         for klass in (Integer, IntegerInherited):
1002             _, _, _, _, default, optional, _, _decoded = values
1003             obj = klass(
1004                 value=value,
1005                 default=default,
1006                 optional=optional,
1007                 _decoded=_decoded,
1008             )
1009             repr(obj)
1010             pprint(obj)
1011             self.assertFalse(obj.expled)
1012             obj_encoded = obj.encode()
1013             obj_expled = obj(value, expl=tag_expl)
1014             self.assertTrue(obj_expled.expled)
1015             repr(obj_expled)
1016             pprint(obj_expled)
1017             obj_expled_encoded = obj_expled.encode()
1018             obj_decoded, tail = obj_expled.decode(
1019                 obj_expled_encoded + tail_junk,
1020                 offset=offset,
1021             )
1022             repr(obj_decoded)
1023             pprint(obj_decoded)
1024             self.assertEqual(tail, tail_junk)
1025             self.assertEqual(obj_decoded, obj_expled)
1026             self.assertNotEqual(obj_decoded, obj)
1027             self.assertEqual(int(obj_decoded), int(obj_expled))
1028             self.assertEqual(int(obj_decoded), int(obj))
1029             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1030             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1031             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1032             self.assertEqual(
1033                 obj_decoded.expl_llen,
1034                 len(len_encode(len(obj_encoded))),
1035             )
1036             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1037             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1038             self.assertEqual(
1039                 obj_decoded.offset,
1040                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1041             )
1042             self.assertEqual(obj_decoded.expl_offset, offset)
1043
1044     def test_go_vectors_valid(self):
1045         for data, expect in ((
1046                 (b"\x00", 0),
1047                 (b"\x7f", 127),
1048                 (b"\x80", -128),
1049                 (b"\xff\x7f", -129),
1050                 (b"\xff", -1),
1051                 (b"\x01", 1),
1052                 (b"\x00\xff", 255),
1053                 (b"\xff\x00", -256),
1054                 (b"\x01\x00", 256),
1055                 (b"\x00\x80", 128),
1056                 (b"\x01\x00", 256),
1057                 (b"\x80\x00\x00\x00\x00\x00\x00\x00", -9223372036854775808),
1058                 (b"\x80\x00\x00\x00", -2147483648),
1059         )):
1060             self.assertEqual(
1061                 Integer().decode(b"".join((
1062                     Integer.tag_default,
1063                     len_encode(len(data)),
1064                     data,
1065                 )))[0],
1066                 expect,
1067             )
1068
1069     def test_go_vectors_invalid(self):
1070         for data in ((
1071                 b"\x00\x7f",
1072                 b"\xff\xf0",
1073         )):
1074             with self.assertRaises(DecodeError):
1075                 Integer().decode(b"".join((
1076                     Integer.tag_default,
1077                     len_encode(len(data)),
1078                     data,
1079                 )))
1080
1081
1082 @composite
1083 def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=False):
1084     if schema is None:
1085         schema = ()
1086         if draw(booleans()):
1087             schema = draw(sets(text_letters(), min_size=1, max_size=256))
1088             bits = draw(sets(
1089                 integers(min_value=0, max_value=255),
1090                 min_size=len(schema),
1091                 max_size=len(schema),
1092             ))
1093             schema = list(zip(schema, bits))
1094
1095     def _value(value_required):
1096         if not value_required and draw(booleans()):
1097             return
1098         generation_choice = 0
1099         if value_required:
1100             generation_choice = draw(sampled_from((1, 2, 3)))
1101         if generation_choice == 1 or draw(booleans()):
1102             return "'%s'B" % "".join(draw(lists(
1103                 sampled_from(("0", "1")),
1104                 max_size=len(schema),
1105             )))
1106         elif generation_choice == 2 or draw(booleans()):
1107             return draw(binary(max_size=len(schema) // 8))
1108         elif generation_choice == 3 or draw(booleans()):
1109             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
1110         return None
1111     value = _value(value_required)
1112     default = _value(value_required=False)
1113     impl = None
1114     expl = None
1115     if do_expl:
1116         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1117     else:
1118         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1119     optional = draw(one_of(none(), booleans()))
1120     _decoded = (
1121         draw(integers(min_value=0)),
1122         draw(integers(min_value=0)),
1123         draw(integers(min_value=0)),
1124     )
1125     return (schema, value, impl, expl, default, optional, _decoded)
1126
1127
1128 class BitStringInherited(BitString):
1129     pass
1130
1131
1132 class TestBitString(CommonMixin, TestCase):
1133     base_klass = BitString
1134
1135     @given(lists(booleans()))
1136     def test_b_encoding(self, bits):
1137         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1138         self.assertEqual(obj.bit_len, len(bits))
1139         self.assertSequenceEqual(list(obj), bits)
1140         for i, bit in enumerate(bits):
1141             self.assertEqual(obj[i], bit)
1142
1143     @given(lists(booleans()))
1144     def test_out_of_bounds_bits(self, bits):
1145         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1146         for i in range(len(bits), len(bits) * 2):
1147             self.assertFalse(obj[i])
1148
1149     def test_bad_b_encoding(self):
1150         with self.assertRaises(ValueError):
1151             BitString("'010120101'B")
1152
1153     @given(
1154         integers(min_value=1, max_value=255),
1155         integers(min_value=1, max_value=255),
1156     )
1157     def test_named_are_stripped(self, leading_zeros, trailing_zeros):
1158         obj = BitString("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1159         self.assertEqual(obj.bit_len, leading_zeros + 1 + trailing_zeros)
1160         self.assertGreater(len(obj.encode()), (leading_zeros + 1 + trailing_zeros) // 8)
1161
1162         class BS(BitString):
1163             schema = (("whatever", 0),)
1164         obj = BS("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1165         self.assertEqual(obj.bit_len, leading_zeros + 1)
1166         self.assertGreater(len(obj.encode()), (leading_zeros + 1) // 8)
1167
1168     def test_zero_len(self):
1169         with self.assertRaises(NotEnoughData):
1170             BitString().decode(b"".join((
1171                 BitString.tag_default,
1172                 len_encode(0),
1173             )))
1174
1175     def test_invalid_value_type(self):
1176         with self.assertRaises(InvalidValueType) as err:
1177             BitString(123)
1178         repr(err.exception)
1179         with self.assertRaises(InvalidValueType) as err:
1180             BitString(u"123")
1181         repr(err.exception)
1182
1183     def test_obj_unknown(self):
1184         with self.assertRaises(ObjUnknown) as err:
1185             BitString(b"whatever")["whenever"]
1186         repr(err.exception)
1187
1188     def test_get_invalid_type(self):
1189         with self.assertRaises(InvalidValueType) as err:
1190             BitString(b"whatever")[(1, 2, 3)]
1191         repr(err.exception)
1192
1193     @given(data_strategy())
1194     def test_unknown_name(self, d):
1195         _schema = d.draw(sets(text_letters(), min_size=2, max_size=5))
1196         missing = _schema.pop()
1197
1198         class BS(BitString):
1199             schema = [(n, i) for i, n in enumerate(_schema)]
1200         with self.assertRaises(ObjUnknown) as err:
1201             BS((missing,))
1202         repr(err.exception)
1203
1204     @given(booleans())
1205     def test_optional(self, optional):
1206         obj = BitString(default=BitString(b""), optional=optional)
1207         self.assertTrue(obj.optional)
1208
1209     @given(binary())
1210     def test_ready(self, value):
1211         obj = BitString()
1212         self.assertFalse(obj.ready)
1213         repr(obj)
1214         pprint(obj)
1215         with self.assertRaises(ObjNotReady) as err:
1216             obj.encode()
1217         repr(err.exception)
1218         obj = BitString(value)
1219         self.assertTrue(obj.ready)
1220         repr(obj)
1221         pprint(obj)
1222
1223     @given(
1224         tuples(integers(min_value=0), binary()),
1225         tuples(integers(min_value=0), binary()),
1226         binary(min_size=1),
1227         binary(min_size=1),
1228     )
1229     def test_comparison(self, value1, value2, tag1, tag2):
1230         for klass in (BitString, BitStringInherited):
1231             obj1 = klass(value1)
1232             obj2 = klass(value2)
1233             self.assertEqual(obj1 == obj2, value1 == value2)
1234             self.assertEqual(obj1 != obj2, value1 != value2)
1235             self.assertEqual(obj1 == bytes(obj2), value1[1] == value2[1])
1236             obj1 = klass(value1, impl=tag1)
1237             obj2 = klass(value1, impl=tag2)
1238             self.assertEqual(obj1 == obj2, tag1 == tag2)
1239             self.assertEqual(obj1 != obj2, tag1 != tag2)
1240
1241     @given(data_strategy())
1242     def test_call(self, d):
1243         for klass in (BitString, BitStringInherited):
1244             (
1245                 schema_initial,
1246                 value_initial,
1247                 impl_initial,
1248                 expl_initial,
1249                 default_initial,
1250                 optional_initial,
1251                 _decoded_initial,
1252             ) = d.draw(bit_string_values_strategy())
1253
1254             class BS(klass):
1255                 schema = schema_initial
1256             obj_initial = BS(
1257                 value=value_initial,
1258                 impl=impl_initial,
1259                 expl=expl_initial,
1260                 default=default_initial,
1261                 optional=optional_initial or False,
1262                 _decoded=_decoded_initial,
1263             )
1264             (
1265                 _,
1266                 value,
1267                 impl,
1268                 expl,
1269                 default,
1270                 optional,
1271                 _decoded,
1272             ) = d.draw(bit_string_values_strategy(
1273                 schema=schema_initial,
1274                 do_expl=impl_initial is None,
1275             ))
1276             obj = obj_initial(
1277                 value=value,
1278                 impl=impl,
1279                 expl=expl,
1280                 default=default,
1281                 optional=optional,
1282             )
1283             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1284             self.assertEqual(obj.expl_tag, expl or expl_initial)
1285             if obj.default is None:
1286                 optional = optional_initial if optional is None else optional
1287                 optional = False if optional is None else optional
1288             else:
1289                 optional = True
1290             self.assertEqual(obj.optional, optional)
1291             self.assertEqual(obj.specs, obj_initial.specs)
1292
1293     @given(bit_string_values_strategy())
1294     def test_copy(self, values):
1295         for klass in (BitString, BitStringInherited):
1296             _schema, value, impl, expl, default, optional, _decoded = values
1297
1298             class BS(klass):
1299                 schema = _schema
1300             obj = BS(
1301                 value=value,
1302                 impl=impl,
1303                 expl=expl,
1304                 default=default,
1305                 optional=optional or False,
1306                 _decoded=_decoded,
1307             )
1308             obj_copied = obj.copy()
1309             self.assert_copied_basic_fields(obj, obj_copied)
1310             self.assertEqual(obj.specs, obj_copied.specs)
1311             self.assertEqual(obj._value, obj_copied._value)
1312
1313     @given(
1314         binary(),
1315         integers(min_value=1).map(tag_encode),
1316     )
1317     def test_stripped(self, value, tag_impl):
1318         obj = BitString(value, impl=tag_impl)
1319         with self.assertRaises(NotEnoughData):
1320             obj.decode(obj.encode()[:-1])
1321
1322     @given(
1323         binary(),
1324         integers(min_value=1).map(tag_ctxc),
1325     )
1326     def test_stripped_expl(self, value, tag_expl):
1327         obj = BitString(value, expl=tag_expl)
1328         with self.assertRaises(NotEnoughData):
1329             obj.decode(obj.encode()[:-1])
1330
1331     @given(
1332         integers(min_value=31),
1333         integers(min_value=0),
1334         decode_path_strat,
1335     )
1336     def test_bad_tag(self, tag, offset, decode_path):
1337         with self.assertRaises(DecodeError) as err:
1338             BitString().decode(
1339                 tag_encode(tag)[:-1],
1340                 offset=offset,
1341                 decode_path=decode_path,
1342             )
1343         repr(err.exception)
1344         self.assertEqual(err.exception.offset, offset)
1345         self.assertEqual(err.exception.decode_path, decode_path)
1346
1347     @given(
1348         integers(min_value=128),
1349         integers(min_value=0),
1350         decode_path_strat,
1351     )
1352     def test_bad_len(self, l, offset, decode_path):
1353         with self.assertRaises(DecodeError) as err:
1354             BitString().decode(
1355                 BitString.tag_default + len_encode(l)[:-1],
1356                 offset=offset,
1357                 decode_path=decode_path,
1358             )
1359         repr(err.exception)
1360         self.assertEqual(err.exception.offset, offset)
1361         self.assertEqual(err.exception.decode_path, decode_path)
1362
1363     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1364     @given(data_strategy())
1365     def test_symmetric(self, d):
1366         (
1367             _schema,
1368             value,
1369             _,
1370             _,
1371             default,
1372             optional,
1373             _decoded,
1374         ) = d.draw(bit_string_values_strategy(value_required=True))
1375         tail_junk = d.draw(binary(max_size=5))
1376         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
1377         offset = d.draw(integers(min_value=0))
1378         for klass in (BitString, BitStringInherited):
1379             class BS(klass):
1380                 schema = _schema
1381             obj = BS(
1382                 value=value,
1383                 default=default,
1384                 optional=optional,
1385                 _decoded=_decoded,
1386             )
1387             repr(obj)
1388             pprint(obj)
1389             self.assertFalse(obj.expled)
1390             obj_encoded = obj.encode()
1391             obj_expled = obj(value, expl=tag_expl)
1392             self.assertTrue(obj_expled.expled)
1393             repr(obj_expled)
1394             pprint(obj_expled)
1395             obj_expled_encoded = obj_expled.encode()
1396             obj_decoded, tail = obj_expled.decode(
1397                 obj_expled_encoded + tail_junk,
1398                 offset=offset,
1399             )
1400             repr(obj_decoded)
1401             pprint(obj_decoded)
1402             self.assertEqual(tail, tail_junk)
1403             self.assertEqual(obj_decoded, obj_expled)
1404             self.assertNotEqual(obj_decoded, obj)
1405             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
1406             self.assertEqual(bytes(obj_decoded), bytes(obj))
1407             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1408             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1409             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1410             self.assertEqual(
1411                 obj_decoded.expl_llen,
1412                 len(len_encode(len(obj_encoded))),
1413             )
1414             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1415             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1416             self.assertEqual(
1417                 obj_decoded.offset,
1418                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1419             )
1420             self.assertEqual(obj_decoded.expl_offset, offset)
1421             if isinstance(value, tuple):
1422                 self.assertSetEqual(set(value), set(obj_decoded.named))
1423                 for name in value:
1424                     obj_decoded[name]
1425
1426     @given(integers(min_value=1, max_value=255))
1427     def test_bad_zero_value(self, pad_size):
1428         with self.assertRaises(DecodeError):
1429             BitString().decode(b"".join((
1430                 BitString.tag_default,
1431                 len_encode(1),
1432                 int2byte(pad_size),
1433             )))
1434
1435     def test_go_vectors_invalid(self):
1436         for data in ((
1437                 b"\x07\x01",
1438                 b"\x07\x40",
1439                 b"\x08\x00",
1440         )):
1441             with self.assertRaises(DecodeError):
1442                 BitString().decode(b"".join((
1443                     BitString.tag_default,
1444                     len_encode(2),
1445                     data,
1446                 )))
1447
1448     def test_go_vectors_valid(self):
1449         obj, _ = BitString().decode(b"".join((
1450             BitString.tag_default,
1451             len_encode(1),
1452             b"\x00",
1453         )))
1454         self.assertEqual(bytes(obj), b"")
1455         self.assertEqual(obj.bit_len, 0)
1456
1457         obj, _ = BitString().decode(b"".join((
1458             BitString.tag_default,
1459             len_encode(2),
1460             b"\x07\x00",
1461         )))
1462         self.assertEqual(bytes(obj), b"\x00")
1463         self.assertEqual(obj.bit_len, 1)
1464
1465         obj = BitString((16, b"\x82\x40"))
1466         self.assertTrue(obj[0])
1467         self.assertFalse(obj[1])
1468         self.assertTrue(obj[6])
1469         self.assertTrue(obj[9])
1470         self.assertFalse(obj[17])
1471
1472     @given(
1473         integers(min_value=1, max_value=30),
1474         lists(
1475             one_of(
1476                 binary(min_size=1, max_size=5),
1477                 lists(
1478                     binary(min_size=1, max_size=5),
1479                     min_size=1,
1480                     max_size=3,
1481                 ),
1482             ),
1483             min_size=0,
1484             max_size=3,
1485         ),
1486         lists(booleans(), min_size=1),
1487         binary(),
1488     )
1489     def test_constructed(self, impl, chunk_inputs, chunk_last_bits, junk):
1490         def chunk_constructed(contents):
1491             return (
1492                 tag_encode(form=TagFormConstructed, num=3) +
1493                 LENINDEF +
1494                 b"".join(BitString(content).encode() for content in contents) +
1495                 EOC
1496             )
1497         chunks = []
1498         payload_expected = b""
1499         bit_len_expected = 0
1500         for chunk_input in chunk_inputs:
1501             if isinstance(chunk_input, binary_type):
1502                 chunks.append(BitString(chunk_input).encode())
1503                 payload_expected += chunk_input
1504                 bit_len_expected += len(chunk_input) * 8
1505             else:
1506                 chunks.append(chunk_constructed(chunk_input))
1507                 payload = b"".join(chunk_input)
1508                 payload_expected += payload
1509                 bit_len_expected += len(payload) * 8
1510         chunk_last = BitString("'%s'B" % "".join(
1511             "1" if bit else "0" for bit in chunk_last_bits
1512         ))
1513         payload_expected += bytes(chunk_last)
1514         bit_len_expected += chunk_last.bit_len
1515         encoded_indefinite = (
1516             tag_encode(form=TagFormConstructed, num=impl) +
1517             LENINDEF +
1518             b"".join(chunks) +
1519             chunk_last.encode() +
1520             EOC
1521         )
1522         encoded_definite = (
1523             tag_encode(form=TagFormConstructed, num=impl) +
1524             len_encode(len(b"".join(chunks) + chunk_last.encode())) +
1525             b"".join(chunks) +
1526             chunk_last.encode()
1527         )
1528         with assertRaisesRegex(self, DecodeError, "unallowed BER"):
1529             BitString(impl=tag_encode(impl)).decode(encoded_indefinite)
1530         for lenindef_expected, encoded in (
1531                 (True, encoded_indefinite),
1532                 (False, encoded_definite),
1533         ):
1534             obj, tail = BitString(impl=tag_encode(impl)).decode(
1535                 encoded + junk,
1536                 ctx={"bered": True},
1537             )
1538             self.assertSequenceEqual(tail, junk)
1539             self.assertEqual(obj.bit_len, bit_len_expected)
1540             self.assertSequenceEqual(bytes(obj), payload_expected)
1541             self.assertTrue(obj.bered)
1542             self.assertEqual(obj.lenindef, lenindef_expected)
1543             self.assertEqual(len(encoded), obj.tlvlen)
1544
1545     @given(
1546         integers(min_value=0),
1547         decode_path_strat,
1548     )
1549     def test_ber_definite_too_short(self, offset, decode_path):
1550         with assertRaisesRegex(self, DecodeError, "longer than data") as err:
1551             BitString().decode(
1552                 tag_encode(3, form=TagFormConstructed) + len_encode(1),
1553                 offset=offset,
1554                 decode_path=decode_path,
1555                 ctx={"bered": True},
1556             )
1557         self.assertEqual(err.exception.decode_path, decode_path)
1558         self.assertEqual(err.exception.offset, offset)
1559
1560     @given(
1561         integers(min_value=0),
1562         decode_path_strat,
1563     )
1564     def test_ber_definite_no_data(self, offset, decode_path):
1565         with assertRaisesRegex(self, DecodeError, "zero length") as err:
1566             BitString().decode(
1567                 tag_encode(3, form=TagFormConstructed) + len_encode(0),
1568                 offset=offset,
1569                 decode_path=decode_path,
1570                 ctx={"bered": True},
1571             )
1572         self.assertEqual(err.exception.decode_path, decode_path)
1573         self.assertEqual(err.exception.offset, offset)
1574
1575     @given(
1576         integers(min_value=0),
1577         decode_path_strat,
1578         integers(min_value=1, max_value=3),
1579     )
1580     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
1581         bs = BitString(b"data").encode()
1582         with self.assertRaises(NotEnoughData) as err:
1583             BitString().decode(
1584                 tag_encode(3, form=TagFormConstructed) + LENINDEF + chunks * bs,
1585                 offset=offset,
1586                 decode_path=decode_path,
1587                 ctx={"bered": True},
1588             )
1589         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
1590         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
1591
1592     @given(
1593         integers(min_value=0),
1594         decode_path_strat,
1595         integers(min_value=1, max_value=3),
1596     )
1597     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
1598         bs = BitString(b"data").encode()
1599         bs_longer = BitString(b"data-longer").encode()
1600         with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
1601             BitString().decode(
1602                 (
1603                     tag_encode(3, form=TagFormConstructed) +
1604                     len_encode((chunks + 1) * len(bs)) +
1605                     chunks * bs +
1606                     bs_longer
1607                 ),
1608                 offset=offset,
1609                 decode_path=decode_path,
1610                 ctx={"bered": True},
1611             )
1612         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
1613         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
1614
1615     @given(
1616         integers(min_value=0),
1617         decode_path_strat,
1618     )
1619     def test_ber_indefinite_no_chunks(self, offset, decode_path):
1620         with assertRaisesRegex(self, DecodeError, "no chunks") as err:
1621             BitString().decode(
1622                 tag_encode(3, form=TagFormConstructed) + LENINDEF + EOC,
1623                 offset=offset,
1624                 decode_path=decode_path,
1625                 ctx={"bered": True},
1626             )
1627         self.assertEqual(err.exception.decode_path, decode_path)
1628         self.assertEqual(err.exception.offset, offset)
1629
1630     @given(data_strategy())
1631     def test_ber_indefinite_not_multiple(self, d):
1632         bs_short = BitString("'A'H").encode()
1633         bs_full = BitString("'AA'H").encode()
1634         chunks = [bs_full for _ in range(d.draw(integers(min_value=0, max_value=3)))]
1635         chunks.append(bs_short)
1636         d.draw(permutations(chunks))
1637         chunks.append(bs_short)
1638         offset = d.draw(integers(min_value=0))
1639         decode_path = d.draw(decode_path_strat)
1640         with assertRaisesRegex(self, DecodeError, "multiple of 8 bits") as err:
1641             BitString().decode(
1642                 (
1643                     tag_encode(3, form=TagFormConstructed) +
1644                     LENINDEF +
1645                     b"".join(chunks) +
1646                     EOC
1647                 ),
1648                 offset=offset,
1649                 decode_path=decode_path,
1650                 ctx={"bered": True},
1651             )
1652         self.assertEqual(
1653             err.exception.decode_path,
1654             decode_path + (str(chunks.index(bs_short)),),
1655         )
1656         self.assertEqual(
1657             err.exception.offset,
1658             offset + 1 + 1 + chunks.index(bs_short) * len(bs_full),
1659         )
1660
1661     def test_x690_vector(self):
1662         vector = BitString("'0A3B5F291CD'H")
1663         obj, tail = BitString().decode(hexdec("0307040A3B5F291CD0"))
1664         self.assertSequenceEqual(tail, b"")
1665         self.assertEqual(obj, vector)
1666         obj, tail = BitString().decode(
1667             hexdec("23800303000A3B0305045F291CD00000"),
1668             ctx={"bered": True},
1669         )
1670         self.assertSequenceEqual(tail, b"")
1671         self.assertEqual(obj, vector)
1672         self.assertTrue(obj.bered)
1673         self.assertTrue(obj.lenindef)
1674
1675
1676 @composite
1677 def octet_string_values_strategy(draw, do_expl=False):
1678     bound_min, bound_max = sorted(draw(sets(
1679         integers(min_value=0, max_value=1 << 7),
1680         min_size=2,
1681         max_size=2,
1682     )))
1683     value = draw(one_of(
1684         none(),
1685         binary(min_size=bound_min, max_size=bound_max),
1686     ))
1687     default = draw(one_of(
1688         none(),
1689         binary(min_size=bound_min, max_size=bound_max),
1690     ))
1691     bounds = None
1692     if draw(booleans()):
1693         bounds = (bound_min, bound_max)
1694     impl = None
1695     expl = None
1696     if do_expl:
1697         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1698     else:
1699         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1700     optional = draw(one_of(none(), booleans()))
1701     _decoded = (
1702         draw(integers(min_value=0)),
1703         draw(integers(min_value=0)),
1704         draw(integers(min_value=0)),
1705     )
1706     return (value, bounds, impl, expl, default, optional, _decoded)
1707
1708
1709 class OctetStringInherited(OctetString):
1710     pass
1711
1712
1713 class TestOctetString(CommonMixin, TestCase):
1714     base_klass = OctetString
1715
1716     def test_invalid_value_type(self):
1717         with self.assertRaises(InvalidValueType) as err:
1718             OctetString(text_type(123))
1719         repr(err.exception)
1720
1721     @given(booleans())
1722     def test_optional(self, optional):
1723         obj = OctetString(default=OctetString(b""), optional=optional)
1724         self.assertTrue(obj.optional)
1725
1726     @given(binary())
1727     def test_ready(self, value):
1728         obj = OctetString()
1729         self.assertFalse(obj.ready)
1730         repr(obj)
1731         pprint(obj)
1732         with self.assertRaises(ObjNotReady) as err:
1733             obj.encode()
1734         repr(err.exception)
1735         obj = OctetString(value)
1736         self.assertTrue(obj.ready)
1737         repr(obj)
1738         pprint(obj)
1739
1740     @given(binary(), binary(), binary(min_size=1), binary(min_size=1))
1741     def test_comparison(self, value1, value2, tag1, tag2):
1742         for klass in (OctetString, OctetStringInherited):
1743             obj1 = klass(value1)
1744             obj2 = klass(value2)
1745             self.assertEqual(obj1 == obj2, value1 == value2)
1746             self.assertEqual(obj1 != obj2, value1 != value2)
1747             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
1748             obj1 = klass(value1, impl=tag1)
1749             obj2 = klass(value1, impl=tag2)
1750             self.assertEqual(obj1 == obj2, tag1 == tag2)
1751             self.assertEqual(obj1 != obj2, tag1 != tag2)
1752
1753     @given(lists(binary()))
1754     def test_sorted_works(self, values):
1755         self.assertSequenceEqual(
1756             [bytes(v) for v in sorted(OctetString(v) for v in values)],
1757             sorted(values),
1758         )
1759
1760     @given(data_strategy())
1761     def test_bounds_satisfied(self, d):
1762         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
1763         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1764         value = d.draw(binary(min_size=bound_min, max_size=bound_max))
1765         OctetString(value=value, bounds=(bound_min, bound_max))
1766
1767     @given(data_strategy())
1768     def test_bounds_unsatisfied(self, d):
1769         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
1770         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1771         value = d.draw(binary(max_size=bound_min - 1))
1772         with self.assertRaises(BoundsError) as err:
1773             OctetString(value=value, bounds=(bound_min, bound_max))
1774         repr(err.exception)
1775         with assertRaisesRegex(self, DecodeError, "bounds") as err:
1776             OctetString(bounds=(bound_min, bound_max)).decode(
1777                 OctetString(value).encode()
1778             )
1779         repr(err.exception)
1780         value = d.draw(binary(min_size=bound_max + 1))
1781         with self.assertRaises(BoundsError) as err:
1782             OctetString(value=value, bounds=(bound_min, bound_max))
1783         repr(err.exception)
1784         with assertRaisesRegex(self, DecodeError, "bounds") as err:
1785             OctetString(bounds=(bound_min, bound_max)).decode(
1786                 OctetString(value).encode()
1787             )
1788         repr(err.exception)
1789
1790     @given(data_strategy())
1791     def test_call(self, d):
1792         for klass in (OctetString, OctetStringInherited):
1793             (
1794                 value_initial,
1795                 bounds_initial,
1796                 impl_initial,
1797                 expl_initial,
1798                 default_initial,
1799                 optional_initial,
1800                 _decoded_initial,
1801             ) = d.draw(octet_string_values_strategy())
1802             obj_initial = klass(
1803                 value_initial,
1804                 bounds_initial,
1805                 impl_initial,
1806                 expl_initial,
1807                 default_initial,
1808                 optional_initial or False,
1809                 _decoded_initial,
1810             )
1811             (
1812                 value,
1813                 bounds,
1814                 impl,
1815                 expl,
1816                 default,
1817                 optional,
1818                 _decoded,
1819             ) = d.draw(octet_string_values_strategy(do_expl=impl_initial is None))
1820             if (default is None) and (obj_initial.default is not None):
1821                 bounds = None
1822             if (
1823                     (bounds is None) and
1824                     (value is not None) and
1825                     (bounds_initial is not None) and
1826                     not (bounds_initial[0] <= len(value) <= bounds_initial[1])
1827             ):
1828                 value = None
1829             if (
1830                     (bounds is None) and
1831                     (default is not None) and
1832                     (bounds_initial is not None) and
1833                     not (bounds_initial[0] <= len(default) <= bounds_initial[1])
1834             ):
1835                 default = None
1836             obj = obj_initial(value, bounds, impl, expl, default, optional)
1837             if obj.ready:
1838                 value_expected = default if value is None else value
1839                 value_expected = (
1840                     default_initial if value_expected is None
1841                     else value_expected
1842                 )
1843                 self.assertEqual(obj, value_expected)
1844             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1845             self.assertEqual(obj.expl_tag, expl or expl_initial)
1846             self.assertEqual(
1847                 obj.default,
1848                 default_initial if default is None else default,
1849             )
1850             if obj.default is None:
1851                 optional = optional_initial if optional is None else optional
1852                 optional = False if optional is None else optional
1853             else:
1854                 optional = True
1855             self.assertEqual(obj.optional, optional)
1856             self.assertEqual(
1857                 (obj._bound_min, obj._bound_max),
1858                 bounds or bounds_initial or (0, float("+inf")),
1859             )
1860
1861     @given(octet_string_values_strategy())
1862     def test_copy(self, values):
1863         for klass in (OctetString, OctetStringInherited):
1864             obj = klass(*values)
1865             obj_copied = obj.copy()
1866             self.assert_copied_basic_fields(obj, obj_copied)
1867             self.assertEqual(obj._bound_min, obj_copied._bound_min)
1868             self.assertEqual(obj._bound_max, obj_copied._bound_max)
1869             self.assertEqual(obj._value, obj_copied._value)
1870
1871     @given(
1872         binary(),
1873         integers(min_value=1).map(tag_encode),
1874     )
1875     def test_stripped(self, value, tag_impl):
1876         obj = OctetString(value, impl=tag_impl)
1877         with self.assertRaises(NotEnoughData):
1878             obj.decode(obj.encode()[:-1])
1879
1880     @given(
1881         binary(),
1882         integers(min_value=1).map(tag_ctxc),
1883     )
1884     def test_stripped_expl(self, value, tag_expl):
1885         obj = OctetString(value, expl=tag_expl)
1886         with self.assertRaises(NotEnoughData):
1887             obj.decode(obj.encode()[:-1])
1888
1889     @given(
1890         integers(min_value=31),
1891         integers(min_value=0),
1892         decode_path_strat,
1893     )
1894     def test_bad_tag(self, tag, offset, decode_path):
1895         with self.assertRaises(DecodeError) as err:
1896             OctetString().decode(
1897                 tag_encode(tag)[:-1],
1898                 offset=offset,
1899                 decode_path=decode_path,
1900             )
1901         repr(err.exception)
1902         self.assertEqual(err.exception.offset, offset)
1903         self.assertEqual(err.exception.decode_path, decode_path)
1904
1905     @given(
1906         integers(min_value=128),
1907         integers(min_value=0),
1908         decode_path_strat,
1909     )
1910     def test_bad_len(self, l, offset, decode_path):
1911         with self.assertRaises(DecodeError) as err:
1912             OctetString().decode(
1913                 OctetString.tag_default + len_encode(l)[:-1],
1914                 offset=offset,
1915                 decode_path=decode_path,
1916             )
1917         repr(err.exception)
1918         self.assertEqual(err.exception.offset, offset)
1919         self.assertEqual(err.exception.decode_path, decode_path)
1920
1921     @given(
1922         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
1923         integers(min_value=0),
1924         decode_path_strat,
1925     )
1926     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
1927         value, bound_min = list(sorted(ints))
1928
1929         class String(OctetString):
1930             bounds = (bound_min, bound_min)
1931         with self.assertRaises(DecodeError) as err:
1932             String().decode(
1933                 OctetString(b"\x00" * value).encode(),
1934                 offset=offset,
1935                 decode_path=decode_path,
1936             )
1937         repr(err.exception)
1938         self.assertEqual(err.exception.offset, offset)
1939         self.assertEqual(err.exception.decode_path, decode_path)
1940
1941     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1942     @given(
1943         octet_string_values_strategy(),
1944         binary(),
1945         integers(min_value=1).map(tag_ctxc),
1946         integers(min_value=0),
1947         binary(max_size=5),
1948     )
1949     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
1950         for klass in (OctetString, OctetStringInherited):
1951             _, _, _, _, default, optional, _decoded = values
1952             obj = klass(
1953                 value=value,
1954                 default=default,
1955                 optional=optional,
1956                 _decoded=_decoded,
1957             )
1958             repr(obj)
1959             pprint(obj)
1960             self.assertFalse(obj.expled)
1961             obj_encoded = obj.encode()
1962             obj_expled = obj(value, expl=tag_expl)
1963             self.assertTrue(obj_expled.expled)
1964             repr(obj_expled)
1965             pprint(obj_expled)
1966             obj_expled_encoded = obj_expled.encode()
1967             obj_decoded, tail = obj_expled.decode(
1968                 obj_expled_encoded + tail_junk,
1969                 offset=offset,
1970             )
1971             repr(obj_decoded)
1972             pprint(obj_decoded)
1973             self.assertEqual(tail, tail_junk)
1974             self.assertEqual(obj_decoded, obj_expled)
1975             self.assertNotEqual(obj_decoded, obj)
1976             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
1977             self.assertEqual(bytes(obj_decoded), bytes(obj))
1978             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1979             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1980             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1981             self.assertEqual(
1982                 obj_decoded.expl_llen,
1983                 len(len_encode(len(obj_encoded))),
1984             )
1985             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1986             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1987             self.assertEqual(
1988                 obj_decoded.offset,
1989                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1990             )
1991             self.assertEqual(obj_decoded.expl_offset, offset)
1992
1993     @given(
1994         integers(min_value=1, max_value=30),
1995         lists(
1996             one_of(
1997                 binary(min_size=1, max_size=5),
1998                 lists(
1999                     binary(min_size=1, max_size=5),
2000                     min_size=1,
2001                     max_size=3,
2002                 ),
2003             ),
2004             min_size=1,
2005             max_size=3,
2006         ),
2007         binary(),
2008     )
2009     def test_constructed(self, impl, chunk_inputs, junk):
2010         def chunk_constructed(contents):
2011             return (
2012                 tag_encode(form=TagFormConstructed, num=4) +
2013                 LENINDEF +
2014                 b"".join(OctetString(content).encode() for content in contents) +
2015                 EOC
2016             )
2017         chunks = []
2018         payload_expected = b""
2019         for chunk_input in chunk_inputs:
2020             if isinstance(chunk_input, binary_type):
2021                 chunks.append(OctetString(chunk_input).encode())
2022                 payload_expected += chunk_input
2023             else:
2024                 chunks.append(chunk_constructed(chunk_input))
2025                 payload = b"".join(chunk_input)
2026                 payload_expected += payload
2027         encoded_indefinite = (
2028             tag_encode(form=TagFormConstructed, num=impl) +
2029             LENINDEF +
2030             b"".join(chunks) +
2031             EOC
2032         )
2033         encoded_definite = (
2034             tag_encode(form=TagFormConstructed, num=impl) +
2035             len_encode(len(b"".join(chunks))) +
2036             b"".join(chunks)
2037         )
2038         with assertRaisesRegex(self, DecodeError, "unallowed BER"):
2039             OctetString(impl=tag_encode(impl)).decode(encoded_indefinite)
2040         for lenindef_expected, encoded in (
2041                 (True, encoded_indefinite),
2042                 (False, encoded_definite),
2043         ):
2044             obj, tail = OctetString(impl=tag_encode(impl)).decode(
2045                 encoded + junk,
2046                 ctx={"bered": True},
2047             )
2048             self.assertSequenceEqual(tail, junk)
2049             self.assertSequenceEqual(bytes(obj), payload_expected)
2050             self.assertTrue(obj.bered)
2051             self.assertEqual(obj.lenindef, lenindef_expected)
2052             self.assertEqual(len(encoded), obj.tlvlen)
2053
2054     @given(
2055         integers(min_value=0),
2056         decode_path_strat,
2057     )
2058     def test_ber_definite_too_short(self, offset, decode_path):
2059         with assertRaisesRegex(self, DecodeError, "longer than data") as err:
2060             OctetString().decode(
2061                 tag_encode(4, form=TagFormConstructed) + len_encode(1),
2062                 offset=offset,
2063                 decode_path=decode_path,
2064                 ctx={"bered": True},
2065             )
2066         self.assertEqual(err.exception.decode_path, decode_path)
2067         self.assertEqual(err.exception.offset, offset)
2068
2069     @given(
2070         integers(min_value=0),
2071         decode_path_strat,
2072         integers(min_value=1, max_value=3),
2073     )
2074     def test_ber_indefinite_no_eoc(self, offset, decode_path, chunks):
2075         bs = OctetString(b"data").encode()
2076         with self.assertRaises(NotEnoughData) as err:
2077             OctetString().decode(
2078                 tag_encode(4, form=TagFormConstructed) + LENINDEF + chunks * bs,
2079                 offset=offset,
2080                 decode_path=decode_path,
2081                 ctx={"bered": True},
2082             )
2083         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2084         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2085
2086     @given(
2087         integers(min_value=0),
2088         decode_path_strat,
2089         integers(min_value=1, max_value=3),
2090     )
2091     def test_ber_definite_chunk_out_of_bounds(self, offset, decode_path, chunks):
2092         bs = OctetString(b"data").encode()
2093         bs_longer = OctetString(b"data-longer").encode()
2094         with assertRaisesRegex(self, DecodeError, "chunk out of bounds") as err:
2095             OctetString().decode(
2096                 (
2097                     tag_encode(4, form=TagFormConstructed) +
2098                     len_encode((chunks + 1) * len(bs)) +
2099                     chunks * bs +
2100                     bs_longer
2101                 ),
2102                 offset=offset,
2103                 decode_path=decode_path,
2104                 ctx={"bered": True},
2105             )
2106         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
2107         self.assertEqual(err.exception.offset, offset + 1 + 1 + chunks * len(bs))
2108
2109
2110 @composite
2111 def null_values_strategy(draw, do_expl=False):
2112     impl = None
2113     expl = None
2114     if do_expl:
2115         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2116     else:
2117         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2118     optional = draw(one_of(none(), booleans()))
2119     _decoded = (
2120         draw(integers(min_value=0)),
2121         draw(integers(min_value=0)),
2122         draw(integers(min_value=0)),
2123     )
2124     return (impl, expl, optional, _decoded)
2125
2126
2127 class NullInherited(Null):
2128     pass
2129
2130
2131 class TestNull(CommonMixin, TestCase):
2132     base_klass = Null
2133
2134     def test_ready(self):
2135         obj = Null()
2136         self.assertTrue(obj.ready)
2137         repr(obj)
2138         pprint(obj)
2139
2140     @given(binary(), binary())
2141     def test_comparison(self, tag1, tag2):
2142         for klass in (Null, NullInherited):
2143             obj1 = klass(impl=tag1)
2144             obj2 = klass(impl=tag2)
2145             self.assertEqual(obj1 == obj2, tag1 == tag2)
2146             self.assertEqual(obj1 != obj2, tag1 != tag2)
2147             self.assertNotEqual(obj1, tag2)
2148
2149     @given(data_strategy())
2150     def test_call(self, d):
2151         for klass in (Null, NullInherited):
2152             (
2153                 impl_initial,
2154                 expl_initial,
2155                 optional_initial,
2156                 _decoded_initial,
2157             ) = d.draw(null_values_strategy())
2158             obj_initial = klass(
2159                 impl=impl_initial,
2160                 expl=expl_initial,
2161                 optional=optional_initial or False,
2162                 _decoded=_decoded_initial,
2163             )
2164             (
2165                 impl,
2166                 expl,
2167                 optional,
2168                 _decoded,
2169             ) = d.draw(null_values_strategy(do_expl=impl_initial is None))
2170             obj = obj_initial(impl=impl, expl=expl, optional=optional)
2171             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2172             self.assertEqual(obj.expl_tag, expl or expl_initial)
2173             optional = optional_initial if optional is None else optional
2174             optional = False if optional is None else optional
2175             self.assertEqual(obj.optional, optional)
2176
2177     @given(null_values_strategy())
2178     def test_copy(self, values):
2179         for klass in (Null, NullInherited):
2180             impl, expl, optional, _decoded = values
2181             obj = klass(
2182                 impl=impl,
2183                 expl=expl,
2184                 optional=optional or False,
2185                 _decoded=_decoded,
2186             )
2187             obj_copied = obj.copy()
2188             self.assert_copied_basic_fields(obj, obj_copied)
2189
2190     @given(integers(min_value=1).map(tag_encode))
2191     def test_stripped(self, tag_impl):
2192         obj = Null(impl=tag_impl)
2193         with self.assertRaises(NotEnoughData):
2194             obj.decode(obj.encode()[:-1])
2195
2196     @given(integers(min_value=1).map(tag_ctxc))
2197     def test_stripped_expl(self, tag_expl):
2198         obj = Null(expl=tag_expl)
2199         with self.assertRaises(NotEnoughData):
2200             obj.decode(obj.encode()[:-1])
2201
2202     @given(
2203         integers(min_value=31),
2204         integers(min_value=0),
2205         decode_path_strat,
2206     )
2207     def test_bad_tag(self, tag, offset, decode_path):
2208         with self.assertRaises(DecodeError) as err:
2209             Null().decode(
2210                 tag_encode(tag)[:-1],
2211                 offset=offset,
2212                 decode_path=decode_path,
2213             )
2214         repr(err.exception)
2215         self.assertEqual(err.exception.offset, offset)
2216         self.assertEqual(err.exception.decode_path, decode_path)
2217
2218     @given(
2219         integers(min_value=128),
2220         integers(min_value=0),
2221         decode_path_strat,
2222     )
2223     def test_bad_len(self, l, offset, decode_path):
2224         with self.assertRaises(DecodeError) as err:
2225             Null().decode(
2226                 Null.tag_default + len_encode(l)[:-1],
2227                 offset=offset,
2228                 decode_path=decode_path,
2229             )
2230         repr(err.exception)
2231         self.assertEqual(err.exception.offset, offset)
2232         self.assertEqual(err.exception.decode_path, decode_path)
2233
2234     @given(binary(min_size=1))
2235     def test_tag_mismatch(self, impl):
2236         assume(impl != Null.tag_default)
2237         with self.assertRaises(TagMismatch):
2238             Null(impl=impl).decode(Null().encode())
2239
2240     @given(
2241         null_values_strategy(),
2242         integers(min_value=1).map(tag_ctxc),
2243         integers(min_value=0),
2244         binary(max_size=5),
2245     )
2246     def test_symmetric(self, values, tag_expl, offset, tail_junk):
2247         for klass in (Null, NullInherited):
2248             _, _, optional, _decoded = values
2249             obj = klass(optional=optional, _decoded=_decoded)
2250             repr(obj)
2251             pprint(obj)
2252             self.assertFalse(obj.expled)
2253             obj_encoded = obj.encode()
2254             obj_expled = obj(expl=tag_expl)
2255             self.assertTrue(obj_expled.expled)
2256             repr(obj_expled)
2257             pprint(obj_expled)
2258             obj_expled_encoded = obj_expled.encode()
2259             obj_decoded, tail = obj_expled.decode(
2260                 obj_expled_encoded + tail_junk,
2261                 offset=offset,
2262             )
2263             repr(obj_decoded)
2264             pprint(obj_decoded)
2265             self.assertEqual(tail, tail_junk)
2266             self.assertEqual(obj_decoded, obj_expled)
2267             self.assertNotEqual(obj_decoded, obj)
2268             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2269             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2270             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2271             self.assertEqual(
2272                 obj_decoded.expl_llen,
2273                 len(len_encode(len(obj_encoded))),
2274             )
2275             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2276             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2277             self.assertEqual(
2278                 obj_decoded.offset,
2279                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2280             )
2281             self.assertEqual(obj_decoded.expl_offset, offset)
2282
2283     @given(integers(min_value=1))
2284     def test_invalid_len(self, l):
2285         with self.assertRaises(InvalidLength):
2286             Null().decode(b"".join((
2287                 Null.tag_default,
2288                 len_encode(l),
2289             )))
2290
2291
2292 @composite
2293 def oid_strategy(draw):
2294     first_arc = draw(integers(min_value=0, max_value=2))
2295     second_arc = 0
2296     if first_arc in (0, 1):
2297         second_arc = draw(integers(min_value=0, max_value=39))
2298     else:
2299         second_arc = draw(integers(min_value=0))
2300     other_arcs = draw(lists(integers(min_value=0)))
2301     return tuple([first_arc, second_arc] + other_arcs)
2302
2303
2304 @composite
2305 def oid_values_strategy(draw, do_expl=False):
2306     value = draw(one_of(none(), oid_strategy()))
2307     impl = None
2308     expl = None
2309     if do_expl:
2310         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2311     else:
2312         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2313     default = draw(one_of(none(), oid_strategy()))
2314     optional = draw(one_of(none(), booleans()))
2315     _decoded = (
2316         draw(integers(min_value=0)),
2317         draw(integers(min_value=0)),
2318         draw(integers(min_value=0)),
2319     )
2320     return (value, impl, expl, default, optional, _decoded)
2321
2322
2323 class ObjectIdentifierInherited(ObjectIdentifier):
2324     pass
2325
2326
2327 class TestObjectIdentifier(CommonMixin, TestCase):
2328     base_klass = ObjectIdentifier
2329
2330     def test_invalid_value_type(self):
2331         with self.assertRaises(InvalidValueType) as err:
2332             ObjectIdentifier(123)
2333         repr(err.exception)
2334
2335     @given(booleans())
2336     def test_optional(self, optional):
2337         obj = ObjectIdentifier(default=ObjectIdentifier("1.2.3"), optional=optional)
2338         self.assertTrue(obj.optional)
2339
2340     @given(oid_strategy())
2341     def test_ready(self, value):
2342         obj = ObjectIdentifier()
2343         self.assertFalse(obj.ready)
2344         repr(obj)
2345         pprint(obj)
2346         with self.assertRaises(ObjNotReady) as err:
2347             obj.encode()
2348         repr(err.exception)
2349         obj = ObjectIdentifier(value)
2350         self.assertTrue(obj.ready)
2351         repr(obj)
2352         pprint(obj)
2353         hash(obj)
2354
2355     @given(oid_strategy(), oid_strategy(), binary(), binary())
2356     def test_comparison(self, value1, value2, tag1, tag2):
2357         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2358             obj1 = klass(value1)
2359             obj2 = klass(value2)
2360             self.assertEqual(obj1 == obj2, value1 == value2)
2361             self.assertEqual(obj1 != obj2, value1 != value2)
2362             self.assertEqual(obj1 == tuple(obj2), value1 == value2)
2363             self.assertEqual(str(obj1) == str(obj2), value1 == value2)
2364             obj1 = klass(value1, impl=tag1)
2365             obj2 = klass(value1, impl=tag2)
2366             self.assertEqual(obj1 == obj2, tag1 == tag2)
2367             self.assertEqual(obj1 != obj2, tag1 != tag2)
2368
2369     @given(lists(oid_strategy()))
2370     def test_sorted_works(self, values):
2371         self.assertSequenceEqual(
2372             [tuple(v) for v in sorted(ObjectIdentifier(v) for v in values)],
2373             sorted(values),
2374         )
2375
2376     @given(data_strategy())
2377     def test_call(self, d):
2378         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2379             (
2380                 value_initial,
2381                 impl_initial,
2382                 expl_initial,
2383                 default_initial,
2384                 optional_initial,
2385                 _decoded_initial,
2386             ) = d.draw(oid_values_strategy())
2387             obj_initial = klass(
2388                 value=value_initial,
2389                 impl=impl_initial,
2390                 expl=expl_initial,
2391                 default=default_initial,
2392                 optional=optional_initial or False,
2393                 _decoded=_decoded_initial,
2394             )
2395             (
2396                 value,
2397                 impl,
2398                 expl,
2399                 default,
2400                 optional,
2401                 _decoded,
2402             ) = d.draw(oid_values_strategy(do_expl=impl_initial is None))
2403             obj = obj_initial(
2404                 value=value,
2405                 impl=impl,
2406                 expl=expl,
2407                 default=default,
2408                 optional=optional,
2409             )
2410             if obj.ready:
2411                 value_expected = default if value is None else value
2412                 value_expected = (
2413                     default_initial if value_expected is None
2414                     else value_expected
2415                 )
2416                 self.assertEqual(obj, value_expected)
2417             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2418             self.assertEqual(obj.expl_tag, expl or expl_initial)
2419             self.assertEqual(
2420                 obj.default,
2421                 default_initial if default is None else default,
2422             )
2423             if obj.default is None:
2424                 optional = optional_initial if optional is None else optional
2425                 optional = False if optional is None else optional
2426             else:
2427                 optional = True
2428             self.assertEqual(obj.optional, optional)
2429
2430     @given(oid_values_strategy())
2431     def test_copy(self, values):
2432         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2433             (
2434                 value,
2435                 impl,
2436                 expl,
2437                 default,
2438                 optional,
2439                 _decoded,
2440             ) = values
2441             obj = klass(
2442                 value=value,
2443                 impl=impl,
2444                 expl=expl,
2445                 default=default,
2446                 optional=optional,
2447                 _decoded=_decoded,
2448             )
2449             obj_copied = obj.copy()
2450             self.assert_copied_basic_fields(obj, obj_copied)
2451             self.assertEqual(obj._value, obj_copied._value)
2452
2453     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2454     @given(
2455         oid_strategy(),
2456         integers(min_value=1).map(tag_encode),
2457     )
2458     def test_stripped(self, value, tag_impl):
2459         obj = ObjectIdentifier(value, impl=tag_impl)
2460         with self.assertRaises(NotEnoughData):
2461             obj.decode(obj.encode()[:-1])
2462
2463     @given(
2464         oid_strategy(),
2465         integers(min_value=1).map(tag_ctxc),
2466     )
2467     def test_stripped_expl(self, value, tag_expl):
2468         obj = ObjectIdentifier(value, expl=tag_expl)
2469         with self.assertRaises(NotEnoughData):
2470             obj.decode(obj.encode()[:-1])
2471
2472     @given(
2473         integers(min_value=31),
2474         integers(min_value=0),
2475         decode_path_strat,
2476     )
2477     def test_bad_tag(self, tag, offset, decode_path):
2478         with self.assertRaises(DecodeError) as err:
2479             ObjectIdentifier().decode(
2480                 tag_encode(tag)[:-1],
2481                 offset=offset,
2482                 decode_path=decode_path,
2483             )
2484         repr(err.exception)
2485         self.assertEqual(err.exception.offset, offset)
2486         self.assertEqual(err.exception.decode_path, decode_path)
2487
2488     @given(
2489         integers(min_value=128),
2490         integers(min_value=0),
2491         decode_path_strat,
2492     )
2493     def test_bad_len(self, l, offset, decode_path):
2494         with self.assertRaises(DecodeError) as err:
2495             ObjectIdentifier().decode(
2496                 ObjectIdentifier.tag_default + len_encode(l)[:-1],
2497                 offset=offset,
2498                 decode_path=decode_path,
2499             )
2500         repr(err.exception)
2501         self.assertEqual(err.exception.offset, offset)
2502         self.assertEqual(err.exception.decode_path, decode_path)
2503
2504     def test_zero_oid(self):
2505         with self.assertRaises(NotEnoughData):
2506             ObjectIdentifier().decode(
2507                 b"".join((ObjectIdentifier.tag_default, len_encode(0)))
2508             )
2509
2510     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2511     @given(oid_strategy())
2512     def test_unfinished_oid(self, value):
2513         assume(list(value)[-1] > 255)
2514         obj_encoded = ObjectIdentifier(value).encode()
2515         obj, _ = ObjectIdentifier().decode(obj_encoded)
2516         data = obj_encoded[obj.tlen + obj.llen:-1]
2517         data = b"".join((
2518             ObjectIdentifier.tag_default,
2519             len_encode(len(data)),
2520             data,
2521         ))
2522         with assertRaisesRegex(self, DecodeError, "unfinished OID"):
2523             obj.decode(data)
2524
2525     @given(integers(min_value=0))
2526     def test_invalid_short(self, value):
2527         with self.assertRaises(InvalidOID):
2528             ObjectIdentifier((value,))
2529         with self.assertRaises(InvalidOID):
2530             ObjectIdentifier("%d" % value)
2531
2532     @given(integers(min_value=3), integers(min_value=0))
2533     def test_invalid_first_arc(self, first_arc, second_arc):
2534         with self.assertRaises(InvalidOID):
2535             ObjectIdentifier((first_arc, second_arc))
2536         with self.assertRaises(InvalidOID):
2537             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2538
2539     @given(integers(min_value=0, max_value=1), integers(min_value=40))
2540     def test_invalid_second_arc(self, first_arc, second_arc):
2541         with self.assertRaises(InvalidOID):
2542             ObjectIdentifier((first_arc, second_arc))
2543         with self.assertRaises(InvalidOID):
2544             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2545
2546     @given(text(alphabet=ascii_letters + ".", min_size=1))
2547     def test_junk(self, oid):
2548         with self.assertRaises(InvalidOID):
2549             ObjectIdentifier(oid)
2550
2551     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2552     @given(oid_strategy())
2553     def test_validness(self, oid):
2554         obj = ObjectIdentifier(oid)
2555         self.assertEqual(obj, ObjectIdentifier(".".join(str(arc) for arc in oid)))
2556         str(obj)
2557         repr(obj)
2558         pprint(obj)
2559
2560     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2561     @given(
2562         oid_values_strategy(),
2563         oid_strategy(),
2564         integers(min_value=1).map(tag_ctxc),
2565         integers(min_value=0),
2566         binary(max_size=5),
2567     )
2568     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
2569         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2570             _, _, _, default, optional, _decoded = values
2571             obj = klass(
2572                 value=value,
2573                 default=default,
2574                 optional=optional,
2575                 _decoded=_decoded,
2576             )
2577             repr(obj)
2578             pprint(obj)
2579             self.assertFalse(obj.expled)
2580             obj_encoded = obj.encode()
2581             obj_expled = obj(value, expl=tag_expl)
2582             self.assertTrue(obj_expled.expled)
2583             repr(obj_expled)
2584             pprint(obj_expled)
2585             obj_expled_encoded = obj_expled.encode()
2586             obj_decoded, tail = obj_expled.decode(
2587                 obj_expled_encoded + tail_junk,
2588                 offset=offset,
2589             )
2590             repr(obj_decoded)
2591             pprint(obj_decoded)
2592             self.assertEqual(tail, tail_junk)
2593             self.assertEqual(obj_decoded, obj_expled)
2594             self.assertNotEqual(obj_decoded, obj)
2595             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
2596             self.assertEqual(tuple(obj_decoded), tuple(obj))
2597             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2598             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2599             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2600             self.assertEqual(
2601                 obj_decoded.expl_llen,
2602                 len(len_encode(len(obj_encoded))),
2603             )
2604             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2605             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2606             self.assertEqual(
2607                 obj_decoded.offset,
2608                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2609             )
2610             self.assertEqual(obj_decoded.expl_offset, offset)
2611
2612     @given(
2613         oid_strategy().map(ObjectIdentifier),
2614         oid_strategy().map(ObjectIdentifier),
2615     )
2616     def test_add(self, oid1, oid2):
2617         oid_expect = ObjectIdentifier(str(oid1) + "." + str(oid2))
2618         for oid_to_add in (oid2, tuple(oid2)):
2619             self.assertEqual(oid1 + oid_to_add, oid_expect)
2620         with self.assertRaises(InvalidValueType):
2621             oid1 + str(oid2)
2622
2623     def test_go_vectors_valid(self):
2624         for data, expect in (
2625                 (b"\x55", (2, 5)),
2626                 (b"\x55\x02", (2, 5, 2)),
2627                 (b"\x55\x02\xc0\x00", (2, 5, 2, 8192)),
2628                 (b"\x81\x34\x03", (2, 100, 3)),
2629         ):
2630             self.assertEqual(
2631                 ObjectIdentifier().decode(b"".join((
2632                     ObjectIdentifier.tag_default,
2633                     len_encode(len(data)),
2634                     data,
2635                 )))[0],
2636                 expect,
2637             )
2638
2639     def test_go_vectors_invalid(self):
2640         data = b"\x55\x02\xc0\x80\x80\x80\x80"
2641         with self.assertRaises(DecodeError):
2642             ObjectIdentifier().decode(b"".join((
2643                 Integer.tag_default,
2644                 len_encode(len(data)),
2645                 data,
2646             )))
2647
2648     def test_x690_vector(self):
2649         self.assertEqual(
2650             ObjectIdentifier().decode(hexdec("0603883703"))[0],
2651             ObjectIdentifier((2, 999, 3)),
2652         )
2653
2654
2655 @composite
2656 def enumerated_values_strategy(draw, schema=None, do_expl=False):
2657     if schema is None:
2658         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
2659         values = list(draw(sets(
2660             integers(),
2661             min_size=len(schema),
2662             max_size=len(schema),
2663         )))
2664         schema = list(zip(schema, values))
2665     value = draw(one_of(none(), sampled_from([k for k, v in schema])))
2666     impl = None
2667     expl = None
2668     if do_expl:
2669         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2670     else:
2671         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2672     default = draw(one_of(none(), sampled_from([v for k, v in schema])))
2673     optional = draw(one_of(none(), booleans()))
2674     _decoded = (
2675         draw(integers(min_value=0)),
2676         draw(integers(min_value=0)),
2677         draw(integers(min_value=0)),
2678     )
2679     return (schema, value, impl, expl, default, optional, _decoded)
2680
2681
2682 class TestEnumerated(CommonMixin, TestCase):
2683     class EWhatever(Enumerated):
2684         schema = (("whatever", 0),)
2685
2686     base_klass = EWhatever
2687
2688     def test_schema_required(self):
2689         with assertRaisesRegex(self, ValueError, "schema must be specified"):
2690             Enumerated()
2691
2692     def test_invalid_value_type(self):
2693         with self.assertRaises(InvalidValueType) as err:
2694             self.base_klass((1, 2))
2695         repr(err.exception)
2696
2697     @given(sets(text_letters(), min_size=2))
2698     def test_unknown_name(self, schema_input):
2699         missing = schema_input.pop()
2700
2701         class E(Enumerated):
2702             schema = [(n, 123) for n in schema_input]
2703         with self.assertRaises(ObjUnknown) as err:
2704             E(missing)
2705         repr(err.exception)
2706
2707     @given(
2708         sets(text_letters(), min_size=2),
2709         sets(integers(), min_size=2),
2710     )
2711     def test_unknown_value(self, schema_input, values_input):
2712         schema_input.pop()
2713         missing_value = values_input.pop()
2714         _input = list(zip(schema_input, values_input))
2715
2716         class E(Enumerated):
2717             schema = _input
2718         with self.assertRaises(DecodeError) as err:
2719             E(missing_value)
2720         repr(err.exception)
2721
2722     @given(booleans())
2723     def test_optional(self, optional):
2724         obj = self.base_klass(default="whatever", optional=optional)
2725         self.assertTrue(obj.optional)
2726
2727     def test_ready(self):
2728         obj = self.base_klass()
2729         self.assertFalse(obj.ready)
2730         repr(obj)
2731         pprint(obj)
2732         with self.assertRaises(ObjNotReady) as err:
2733             obj.encode()
2734         repr(err.exception)
2735         obj = self.base_klass("whatever")
2736         self.assertTrue(obj.ready)
2737         repr(obj)
2738         pprint(obj)
2739
2740     @given(integers(), integers(), binary(), binary())
2741     def test_comparison(self, value1, value2, tag1, tag2):
2742         class E(Enumerated):
2743             schema = (
2744                 ("whatever0", value1),
2745                 ("whatever1", value2),
2746             )
2747
2748         class EInherited(E):
2749             pass
2750         for klass in (E, EInherited):
2751             obj1 = klass(value1)
2752             obj2 = klass(value2)
2753             self.assertEqual(obj1 == obj2, value1 == value2)
2754             self.assertEqual(obj1 != obj2, value1 != value2)
2755             self.assertEqual(obj1 == int(obj2), value1 == value2)
2756             obj1 = klass(value1, impl=tag1)
2757             obj2 = klass(value1, impl=tag2)
2758             self.assertEqual(obj1 == obj2, tag1 == tag2)
2759             self.assertEqual(obj1 != obj2, tag1 != tag2)
2760
2761     @given(data_strategy())
2762     def test_call(self, d):
2763         (
2764             schema_initial,
2765             value_initial,
2766             impl_initial,
2767             expl_initial,
2768             default_initial,
2769             optional_initial,
2770             _decoded_initial,
2771         ) = d.draw(enumerated_values_strategy())
2772
2773         class E(Enumerated):
2774             schema = schema_initial
2775         obj_initial = E(
2776             value=value_initial,
2777             impl=impl_initial,
2778             expl=expl_initial,
2779             default=default_initial,
2780             optional=optional_initial or False,
2781             _decoded=_decoded_initial,
2782         )
2783         (
2784             _,
2785             value,
2786             impl,
2787             expl,
2788             default,
2789             optional,
2790             _decoded,
2791         ) = d.draw(enumerated_values_strategy(
2792             schema=schema_initial,
2793             do_expl=impl_initial is None,
2794         ))
2795         obj = obj_initial(
2796             value=value,
2797             impl=impl,
2798             expl=expl,
2799             default=default,
2800             optional=optional,
2801         )
2802         if obj.ready:
2803             value_expected = default if value is None else value
2804             value_expected = (
2805                 default_initial if value_expected is None
2806                 else value_expected
2807             )
2808             self.assertEqual(
2809                 int(obj),
2810                 dict(schema_initial).get(value_expected, value_expected),
2811             )
2812         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2813         self.assertEqual(obj.expl_tag, expl or expl_initial)
2814         self.assertEqual(
2815             obj.default,
2816             default_initial if default is None else default,
2817         )
2818         if obj.default is None:
2819             optional = optional_initial if optional is None else optional
2820             optional = False if optional is None else optional
2821         else:
2822             optional = True
2823         self.assertEqual(obj.optional, optional)
2824         self.assertEqual(obj.specs, dict(schema_initial))
2825
2826     @given(enumerated_values_strategy())
2827     def test_copy(self, values):
2828         schema_input, value, impl, expl, default, optional, _decoded = values
2829
2830         class E(Enumerated):
2831             schema = schema_input
2832         obj = E(
2833             value=value,
2834             impl=impl,
2835             expl=expl,
2836             default=default,
2837             optional=optional,
2838             _decoded=_decoded,
2839         )
2840         obj_copied = obj.copy()
2841         self.assert_copied_basic_fields(obj, obj_copied)
2842         self.assertEqual(obj.specs, obj_copied.specs)
2843
2844     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2845     @given(data_strategy())
2846     def test_symmetric(self, d):
2847         schema_input, _, _, _, default, optional, _decoded = d.draw(
2848             enumerated_values_strategy(),
2849         )
2850         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
2851         offset = d.draw(integers(min_value=0))
2852         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
2853         tail_junk = d.draw(binary(max_size=5))
2854
2855         class E(Enumerated):
2856             schema = schema_input
2857         obj = E(
2858             value=value,
2859             default=default,
2860             optional=optional,
2861             _decoded=_decoded,
2862         )
2863         repr(obj)
2864         pprint(obj)
2865         self.assertFalse(obj.expled)
2866         obj_encoded = obj.encode()
2867         obj_expled = obj(value, expl=tag_expl)
2868         self.assertTrue(obj_expled.expled)
2869         repr(obj_expled)
2870         pprint(obj_expled)
2871         obj_expled_encoded = obj_expled.encode()
2872         obj_decoded, tail = obj_expled.decode(
2873             obj_expled_encoded + tail_junk,
2874             offset=offset,
2875         )
2876         repr(obj_decoded)
2877         pprint(obj_decoded)
2878         self.assertEqual(tail, tail_junk)
2879         self.assertEqual(obj_decoded, obj_expled)
2880         self.assertNotEqual(obj_decoded, obj)
2881         self.assertEqual(int(obj_decoded), int(obj_expled))
2882         self.assertEqual(int(obj_decoded), int(obj))
2883         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2884         self.assertEqual(obj_decoded.expl_tag, tag_expl)
2885         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2886         self.assertEqual(
2887             obj_decoded.expl_llen,
2888             len(len_encode(len(obj_encoded))),
2889         )
2890         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2891         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2892         self.assertEqual(
2893             obj_decoded.offset,
2894             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2895         )
2896         self.assertEqual(obj_decoded.expl_offset, offset)
2897
2898
2899 @composite
2900 def string_values_strategy(draw, alphabet, do_expl=False):
2901     bound_min, bound_max = sorted(draw(sets(
2902         integers(min_value=0, max_value=1 << 7),
2903         min_size=2,
2904         max_size=2,
2905     )))
2906     value = draw(one_of(
2907         none(),
2908         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
2909     ))
2910     default = draw(one_of(
2911         none(),
2912         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
2913     ))
2914     bounds = None
2915     if draw(booleans()):
2916         bounds = (bound_min, bound_max)
2917     impl = None
2918     expl = None
2919     if do_expl:
2920         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2921     else:
2922         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2923     optional = draw(one_of(none(), booleans()))
2924     _decoded = (
2925         draw(integers(min_value=0)),
2926         draw(integers(min_value=0)),
2927         draw(integers(min_value=0)),
2928     )
2929     return (value, bounds, impl, expl, default, optional, _decoded)
2930
2931
2932 class StringMixin(object):
2933     def test_invalid_value_type(self):
2934         with self.assertRaises(InvalidValueType) as err:
2935             self.base_klass((1, 2))
2936         repr(err.exception)
2937
2938     def text_alphabet(self):
2939         if self.base_klass.encoding in ("ascii", "iso-8859-1"):
2940             return printable + whitespace
2941         return None
2942
2943     @given(booleans())
2944     def test_optional(self, optional):
2945         obj = self.base_klass(default=self.base_klass(""), optional=optional)
2946         self.assertTrue(obj.optional)
2947
2948     @given(data_strategy())
2949     def test_ready(self, d):
2950         obj = self.base_klass()
2951         self.assertFalse(obj.ready)
2952         repr(obj)
2953         pprint(obj)
2954         text_type(obj)
2955         with self.assertRaises(ObjNotReady) as err:
2956             obj.encode()
2957         repr(err.exception)
2958         value = d.draw(text(alphabet=self.text_alphabet()))
2959         obj = self.base_klass(value)
2960         self.assertTrue(obj.ready)
2961         repr(obj)
2962         pprint(obj)
2963         text_type(obj)
2964
2965     @given(data_strategy())
2966     def test_comparison(self, d):
2967         value1 = d.draw(text(alphabet=self.text_alphabet()))
2968         value2 = d.draw(text(alphabet=self.text_alphabet()))
2969         tag1 = d.draw(binary(min_size=1))
2970         tag2 = d.draw(binary(min_size=1))
2971         obj1 = self.base_klass(value1)
2972         obj2 = self.base_klass(value2)
2973         self.assertEqual(obj1 == obj2, value1 == value2)
2974         self.assertEqual(obj1 != obj2, value1 != value2)
2975         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
2976         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
2977         obj1 = self.base_klass(value1, impl=tag1)
2978         obj2 = self.base_klass(value1, impl=tag2)
2979         self.assertEqual(obj1 == obj2, tag1 == tag2)
2980         self.assertEqual(obj1 != obj2, tag1 != tag2)
2981
2982     @given(data_strategy())
2983     def test_bounds_satisfied(self, d):
2984         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
2985         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
2986         value = d.draw(text(
2987             alphabet=self.text_alphabet(),
2988             min_size=bound_min,
2989             max_size=bound_max,
2990         ))
2991         self.base_klass(value=value, bounds=(bound_min, bound_max))
2992
2993     @given(data_strategy())
2994     def test_bounds_unsatisfied(self, d):
2995         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
2996         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
2997         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
2998         with self.assertRaises(BoundsError) as err:
2999             self.base_klass(value=value, bounds=(bound_min, bound_max))
3000         repr(err.exception)
3001         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3002             self.base_klass(bounds=(bound_min, bound_max)).decode(
3003                 self.base_klass(value).encode()
3004             )
3005         repr(err.exception)
3006         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
3007         with self.assertRaises(BoundsError) as err:
3008             self.base_klass(value=value, bounds=(bound_min, bound_max))
3009         repr(err.exception)
3010         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3011             self.base_klass(bounds=(bound_min, bound_max)).decode(
3012                 self.base_klass(value).encode()
3013             )
3014         repr(err.exception)
3015
3016     @given(data_strategy())
3017     def test_call(self, d):
3018         (
3019             value_initial,
3020             bounds_initial,
3021             impl_initial,
3022             expl_initial,
3023             default_initial,
3024             optional_initial,
3025             _decoded_initial,
3026         ) = d.draw(string_values_strategy(self.text_alphabet()))
3027         obj_initial = self.base_klass(
3028             value_initial,
3029             bounds_initial,
3030             impl_initial,
3031             expl_initial,
3032             default_initial,
3033             optional_initial or False,
3034             _decoded_initial,
3035         )
3036         (
3037             value,
3038             bounds,
3039             impl,
3040             expl,
3041             default,
3042             optional,
3043             _decoded,
3044         ) = d.draw(string_values_strategy(
3045             self.text_alphabet(),
3046             do_expl=impl_initial is None,
3047         ))
3048         if (default is None) and (obj_initial.default is not None):
3049             bounds = None
3050         if (
3051                 (bounds is None) and
3052                 (value is not None) and
3053                 (bounds_initial is not None) and
3054                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
3055         ):
3056             value = None
3057         if (
3058                 (bounds is None) and
3059                 (default is not None) and
3060                 (bounds_initial is not None) and
3061                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
3062         ):
3063             default = None
3064         obj = obj_initial(value, bounds, impl, expl, default, optional)
3065         if obj.ready:
3066             value_expected = default if value is None else value
3067             value_expected = (
3068                 default_initial if value_expected is None
3069                 else value_expected
3070             )
3071             self.assertEqual(obj, value_expected)
3072         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3073         self.assertEqual(obj.expl_tag, expl or expl_initial)
3074         self.assertEqual(
3075             obj.default,
3076             default_initial if default is None else default,
3077         )
3078         if obj.default is None:
3079             optional = optional_initial if optional is None else optional
3080             optional = False if optional is None else optional
3081         else:
3082             optional = True
3083         self.assertEqual(obj.optional, optional)
3084         self.assertEqual(
3085             (obj._bound_min, obj._bound_max),
3086             bounds or bounds_initial or (0, float("+inf")),
3087         )
3088
3089     @given(data_strategy())
3090     def test_copy(self, d):
3091         values = d.draw(string_values_strategy(self.text_alphabet()))
3092         obj = self.base_klass(*values)
3093         obj_copied = obj.copy()
3094         self.assert_copied_basic_fields(obj, obj_copied)
3095         self.assertEqual(obj._bound_min, obj_copied._bound_min)
3096         self.assertEqual(obj._bound_max, obj_copied._bound_max)
3097         self.assertEqual(obj._value, obj_copied._value)
3098
3099     @given(data_strategy())
3100     def test_stripped(self, d):
3101         value = d.draw(text(alphabet=self.text_alphabet()))
3102         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3103         obj = self.base_klass(value, impl=tag_impl)
3104         with self.assertRaises(NotEnoughData):
3105             obj.decode(obj.encode()[:-1])
3106
3107     @given(data_strategy())
3108     def test_stripped_expl(self, d):
3109         value = d.draw(text(alphabet=self.text_alphabet()))
3110         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3111         obj = self.base_klass(value, expl=tag_expl)
3112         with self.assertRaises(NotEnoughData):
3113             obj.decode(obj.encode()[:-1])
3114
3115     @given(
3116         integers(min_value=31),
3117         integers(min_value=0),
3118         decode_path_strat,
3119     )
3120     def test_bad_tag(self, tag, offset, decode_path):
3121         with self.assertRaises(DecodeError) as err:
3122             self.base_klass().decode(
3123                 tag_encode(tag)[:-1],
3124                 offset=offset,
3125                 decode_path=decode_path,
3126             )
3127         repr(err.exception)
3128         self.assertEqual(err.exception.offset, offset)
3129         self.assertEqual(err.exception.decode_path, decode_path)
3130
3131     @given(
3132         integers(min_value=128),
3133         integers(min_value=0),
3134         decode_path_strat,
3135     )
3136     def test_bad_len(self, l, offset, decode_path):
3137         with self.assertRaises(DecodeError) as err:
3138             self.base_klass().decode(
3139                 self.base_klass.tag_default + len_encode(l)[:-1],
3140                 offset=offset,
3141                 decode_path=decode_path,
3142             )
3143         repr(err.exception)
3144         self.assertEqual(err.exception.offset, offset)
3145         self.assertEqual(err.exception.decode_path, decode_path)
3146
3147     @given(
3148         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3149         integers(min_value=0),
3150         decode_path_strat,
3151     )
3152     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3153         value, bound_min = list(sorted(ints))
3154
3155         class String(self.base_klass):
3156             # Multiply this value by four, to satisfy UTF-32 bounds
3157             # (4 bytes per character) validation
3158             bounds = (bound_min * 4, bound_min * 4)
3159         with self.assertRaises(DecodeError) as err:
3160             String().decode(
3161                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
3162                 offset=offset,
3163                 decode_path=decode_path,
3164             )
3165         repr(err.exception)
3166         self.assertEqual(err.exception.offset, offset)
3167         self.assertEqual(err.exception.decode_path, decode_path)
3168
3169     @given(data_strategy())
3170     def test_symmetric(self, d):
3171         values = d.draw(string_values_strategy(self.text_alphabet()))
3172         value = d.draw(text(alphabet=self.text_alphabet()))
3173         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3174         offset = d.draw(integers(min_value=0))
3175         tail_junk = d.draw(binary(max_size=5))
3176         _, _, _, _, default, optional, _decoded = values
3177         obj = self.base_klass(
3178             value=value,
3179             default=default,
3180             optional=optional,
3181             _decoded=_decoded,
3182         )
3183         repr(obj)
3184         pprint(obj)
3185         self.assertFalse(obj.expled)
3186         obj_encoded = obj.encode()
3187         obj_expled = obj(value, expl=tag_expl)
3188         self.assertTrue(obj_expled.expled)
3189         repr(obj_expled)
3190         pprint(obj_expled)
3191         obj_expled_encoded = obj_expled.encode()
3192         obj_decoded, tail = obj_expled.decode(
3193             obj_expled_encoded + tail_junk,
3194             offset=offset,
3195         )
3196         repr(obj_decoded)
3197         pprint(obj_decoded)
3198         self.assertEqual(tail, tail_junk)
3199         self.assertEqual(obj_decoded, obj_expled)
3200         self.assertNotEqual(obj_decoded, obj)
3201         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3202         self.assertEqual(bytes(obj_decoded), bytes(obj))
3203         self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
3204         self.assertEqual(text_type(obj_decoded), text_type(obj))
3205         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3206         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3207         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3208         self.assertEqual(
3209             obj_decoded.expl_llen,
3210             len(len_encode(len(obj_encoded))),
3211         )
3212         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3213         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3214         self.assertEqual(
3215             obj_decoded.offset,
3216             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3217         )
3218         self.assertEqual(obj_decoded.expl_offset, offset)
3219
3220
3221 class TestUTF8String(StringMixin, CommonMixin, TestCase):
3222     base_klass = UTF8String
3223
3224
3225 class UnicodeDecodeErrorMixin(object):
3226     @given(text(
3227         alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
3228         min_size=1,
3229         max_size=5,
3230     ))
3231     def test_unicode_decode_error(self, cyrillic_text):
3232         with self.assertRaises(DecodeError):
3233             self.base_klass(cyrillic_text)
3234
3235
3236 class TestNumericString(StringMixin, CommonMixin, TestCase):
3237     base_klass = NumericString
3238
3239     def text_alphabet(self):
3240         return digits
3241
3242     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
3243     def test_non_numeric(self, cyrillic_text):
3244         with assertRaisesRegex(self, DecodeError, "non-numeric"):
3245             self.base_klass(cyrillic_text)
3246
3247     @given(
3248         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3249         integers(min_value=0),
3250         decode_path_strat,
3251     )
3252     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3253         value, bound_min = list(sorted(ints))
3254
3255         class String(self.base_klass):
3256             bounds = (bound_min, bound_min)
3257         with self.assertRaises(DecodeError) as err:
3258             String().decode(
3259                 self.base_klass(b"1" * value).encode(),
3260                 offset=offset,
3261                 decode_path=decode_path,
3262             )
3263         repr(err.exception)
3264         self.assertEqual(err.exception.offset, offset)
3265         self.assertEqual(err.exception.decode_path, decode_path)
3266
3267
3268 class TestPrintableString(
3269         UnicodeDecodeErrorMixin,
3270         StringMixin,
3271         CommonMixin,
3272         TestCase,
3273 ):
3274     base_klass = PrintableString
3275
3276
3277 class TestTeletexString(
3278         UnicodeDecodeErrorMixin,
3279         StringMixin,
3280         CommonMixin,
3281         TestCase,
3282 ):
3283     base_klass = TeletexString
3284
3285
3286 class TestVideotexString(
3287         UnicodeDecodeErrorMixin,
3288         StringMixin,
3289         CommonMixin,
3290         TestCase,
3291 ):
3292     base_klass = VideotexString
3293
3294
3295 class TestIA5String(
3296         UnicodeDecodeErrorMixin,
3297         StringMixin,
3298         CommonMixin,
3299         TestCase,
3300 ):
3301     base_klass = IA5String
3302
3303
3304 class TestGraphicString(
3305         UnicodeDecodeErrorMixin,
3306         StringMixin,
3307         CommonMixin,
3308         TestCase,
3309 ):
3310     base_klass = GraphicString
3311
3312
3313 class TestVisibleString(
3314         UnicodeDecodeErrorMixin,
3315         StringMixin,
3316         CommonMixin,
3317         TestCase,
3318 ):
3319     base_klass = VisibleString
3320
3321     def test_x690_vector(self):
3322         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3323         self.assertSequenceEqual(tail, b"")
3324         self.assertEqual(str(obj), "Jones")
3325         self.assertFalse(obj.bered)
3326         self.assertFalse(obj.lenindef)
3327
3328         obj, tail = VisibleString().decode(
3329             hexdec("3A0904034A6F6E04026573"),
3330             ctx={"bered": True},
3331         )
3332         self.assertSequenceEqual(tail, b"")
3333         self.assertEqual(str(obj), "Jones")
3334         self.assertTrue(obj.bered)
3335         self.assertFalse(obj.lenindef)
3336
3337         obj, tail = VisibleString().decode(
3338             hexdec("3A8004034A6F6E040265730000"),
3339             ctx={"bered": True},
3340         )
3341         self.assertSequenceEqual(tail, b"")
3342         self.assertEqual(str(obj), "Jones")
3343         self.assertTrue(obj.bered)
3344         self.assertTrue(obj.lenindef)
3345
3346
3347 class TestGeneralString(
3348         UnicodeDecodeErrorMixin,
3349         StringMixin,
3350         CommonMixin,
3351         TestCase,
3352 ):
3353     base_klass = GeneralString
3354
3355
3356 class TestUniversalString(StringMixin, CommonMixin, TestCase):
3357     base_klass = UniversalString
3358
3359
3360 class TestBMPString(StringMixin, CommonMixin, TestCase):
3361     base_klass = BMPString
3362
3363
3364 @composite
3365 def generalized_time_values_strategy(
3366         draw,
3367         min_datetime,
3368         max_datetime,
3369         omit_ms=False,
3370         do_expl=False,
3371 ):
3372     value = None
3373     if draw(booleans()):
3374         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3375         if omit_ms:
3376             value = value.replace(microsecond=0)
3377     default = None
3378     if draw(booleans()):
3379         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3380         if omit_ms:
3381             default = default.replace(microsecond=0)
3382     impl = None
3383     expl = None
3384     if do_expl:
3385         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3386     else:
3387         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3388     optional = draw(one_of(none(), booleans()))
3389     _decoded = (
3390         draw(integers(min_value=0)),
3391         draw(integers(min_value=0)),
3392         draw(integers(min_value=0)),
3393     )
3394     return (value, impl, expl, default, optional, _decoded)
3395
3396
3397 class TimeMixin(object):
3398     def test_invalid_value_type(self):
3399         with self.assertRaises(InvalidValueType) as err:
3400             self.base_klass(datetime.now().timetuple())
3401         repr(err.exception)
3402
3403     @given(data_strategy())
3404     def test_optional(self, d):
3405         default = d.draw(datetimes(
3406             min_value=self.min_datetime,
3407             max_value=self.max_datetime,
3408         ))
3409         optional = d.draw(booleans())
3410         obj = self.base_klass(default=default, optional=optional)
3411         self.assertTrue(obj.optional)
3412
3413     @given(data_strategy())
3414     def test_ready(self, d):
3415         obj = self.base_klass()
3416         self.assertFalse(obj.ready)
3417         repr(obj)
3418         pprint(obj)
3419         with self.assertRaises(ObjNotReady) as err:
3420             obj.encode()
3421         repr(err.exception)
3422         value = d.draw(datetimes(min_value=self.min_datetime))
3423         obj = self.base_klass(value)
3424         self.assertTrue(obj.ready)
3425         repr(obj)
3426         pprint(obj)
3427
3428     @given(data_strategy())
3429     def test_comparison(self, d):
3430         value1 = d.draw(datetimes(
3431             min_value=self.min_datetime,
3432             max_value=self.max_datetime,
3433         ))
3434         value2 = d.draw(datetimes(
3435             min_value=self.min_datetime,
3436             max_value=self.max_datetime,
3437         ))
3438         tag1 = d.draw(binary(min_size=1))
3439         tag2 = d.draw(binary(min_size=1))
3440         if self.omit_ms:
3441             value1 = value1.replace(microsecond=0)
3442             value2 = value2.replace(microsecond=0)
3443         obj1 = self.base_klass(value1)
3444         obj2 = self.base_klass(value2)
3445         self.assertEqual(obj1 == obj2, value1 == value2)
3446         self.assertEqual(obj1 != obj2, value1 != value2)
3447         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
3448         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3449         obj1 = self.base_klass(value1, impl=tag1)
3450         obj2 = self.base_klass(value1, impl=tag2)
3451         self.assertEqual(obj1 == obj2, tag1 == tag2)
3452         self.assertEqual(obj1 != obj2, tag1 != tag2)
3453
3454     @given(data_strategy())
3455     def test_call(self, d):
3456         (
3457             value_initial,
3458             impl_initial,
3459             expl_initial,
3460             default_initial,
3461             optional_initial,
3462             _decoded_initial,
3463         ) = d.draw(generalized_time_values_strategy(
3464             min_datetime=self.min_datetime,
3465             max_datetime=self.max_datetime,
3466             omit_ms=self.omit_ms,
3467         ))
3468         obj_initial = self.base_klass(
3469             value=value_initial,
3470             impl=impl_initial,
3471             expl=expl_initial,
3472             default=default_initial,
3473             optional=optional_initial or False,
3474             _decoded=_decoded_initial,
3475         )
3476         (
3477             value,
3478             impl,
3479             expl,
3480             default,
3481             optional,
3482             _decoded,
3483         ) = d.draw(generalized_time_values_strategy(
3484             min_datetime=self.min_datetime,
3485             max_datetime=self.max_datetime,
3486             omit_ms=self.omit_ms,
3487             do_expl=impl_initial is None,
3488         ))
3489         obj = obj_initial(
3490             value=value,
3491             impl=impl,
3492             expl=expl,
3493             default=default,
3494             optional=optional,
3495         )
3496         if obj.ready:
3497             value_expected = default if value is None else value
3498             value_expected = (
3499                 default_initial if value_expected is None
3500                 else value_expected
3501             )
3502             self.assertEqual(obj, value_expected)
3503         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3504         self.assertEqual(obj.expl_tag, expl or expl_initial)
3505         self.assertEqual(
3506             obj.default,
3507             default_initial if default is None else default,
3508         )
3509         if obj.default is None:
3510             optional = optional_initial if optional is None else optional
3511             optional = False if optional is None else optional
3512         else:
3513             optional = True
3514         self.assertEqual(obj.optional, optional)
3515
3516     @given(data_strategy())
3517     def test_copy(self, d):
3518         values = d.draw(generalized_time_values_strategy(
3519             min_datetime=self.min_datetime,
3520             max_datetime=self.max_datetime,
3521         ))
3522         obj = self.base_klass(*values)
3523         obj_copied = obj.copy()
3524         self.assert_copied_basic_fields(obj, obj_copied)
3525         self.assertEqual(obj._value, obj_copied._value)
3526
3527     @given(data_strategy())
3528     def test_stripped(self, d):
3529         value = d.draw(datetimes(
3530             min_value=self.min_datetime,
3531             max_value=self.max_datetime,
3532         ))
3533         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3534         obj = self.base_klass(value, impl=tag_impl)
3535         with self.assertRaises(NotEnoughData):
3536             obj.decode(obj.encode()[:-1])
3537
3538     @given(data_strategy())
3539     def test_stripped_expl(self, d):
3540         value = d.draw(datetimes(
3541             min_value=self.min_datetime,
3542             max_value=self.max_datetime,
3543         ))
3544         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3545         obj = self.base_klass(value, expl=tag_expl)
3546         with self.assertRaises(NotEnoughData):
3547             obj.decode(obj.encode()[:-1])
3548
3549     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3550     @given(data_strategy())
3551     def test_symmetric(self, d):
3552         values = d.draw(generalized_time_values_strategy(
3553             min_datetime=self.min_datetime,
3554             max_datetime=self.max_datetime,
3555         ))
3556         value = d.draw(datetimes(
3557             min_value=self.min_datetime,
3558             max_value=self.max_datetime,
3559         ))
3560         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3561         offset = d.draw(integers(min_value=0))
3562         tail_junk = d.draw(binary(max_size=5))
3563         _, _, _, default, optional, _decoded = values
3564         obj = self.base_klass(
3565             value=value,
3566             default=default,
3567             optional=optional,
3568             _decoded=_decoded,
3569         )
3570         repr(obj)
3571         pprint(obj)
3572         self.assertFalse(obj.expled)
3573         obj_encoded = obj.encode()
3574         obj_expled = obj(value, expl=tag_expl)
3575         self.assertTrue(obj_expled.expled)
3576         repr(obj_expled)
3577         pprint(obj_expled)
3578         obj_expled_encoded = obj_expled.encode()
3579         obj_decoded, tail = obj_expled.decode(
3580             obj_expled_encoded + tail_junk,
3581             offset=offset,
3582         )
3583         repr(obj_decoded)
3584         pprint(obj_decoded)
3585         self.assertEqual(tail, tail_junk)
3586         self.assertEqual(obj_decoded, obj_expled)
3587         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
3588         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
3589         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3590         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3591         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3592         self.assertEqual(
3593             obj_decoded.expl_llen,
3594             len(len_encode(len(obj_encoded))),
3595         )
3596         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3597         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3598         self.assertEqual(
3599             obj_decoded.offset,
3600             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3601         )
3602         self.assertEqual(obj_decoded.expl_offset, offset)
3603
3604
3605 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
3606     base_klass = GeneralizedTime
3607     omit_ms = False
3608     min_datetime = datetime(1900, 1, 1)
3609     max_datetime = datetime(9999, 12, 31)
3610
3611     def test_go_vectors_invalid(self):
3612         for data in ((
3613                 b"20100102030405",
3614                 b"00000100000000Z",
3615                 b"20101302030405Z",
3616                 b"20100002030405Z",
3617                 b"20100100030405Z",
3618                 b"20100132030405Z",
3619                 b"20100231030405Z",
3620                 b"20100102240405Z",
3621                 b"20100102036005Z",
3622                 b"20100102030460Z",
3623                 b"-20100102030410Z",
3624                 b"2010-0102030410Z",
3625                 b"2010-0002030410Z",
3626                 b"201001-02030410Z",
3627                 b"20100102-030410Z",
3628                 b"2010010203-0410Z",
3629                 b"201001020304-10Z",
3630                 # These ones are INVALID in *DER*, but accepted
3631                 # by Go's encoding/asn1
3632                 b"20100102030405+0607",
3633                 b"20100102030405-0607",
3634         )):
3635             with self.assertRaises(DecodeError) as err:
3636                 GeneralizedTime(data)
3637             repr(err.exception)
3638
3639     def test_go_vectors_valid(self):
3640         self.assertEqual(
3641             GeneralizedTime(b"20100102030405Z").todatetime(),
3642             datetime(2010, 1, 2, 3, 4, 5, 0),
3643         )
3644
3645     @given(
3646         binary(
3647             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3648             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3649         ),
3650         binary(min_size=1, max_size=1),
3651         binary(
3652             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3653             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3654         ),
3655     )
3656     def test_junk(self, part0, part1, part2):
3657         junk = part0 + part1 + part2
3658         assume(not (set(junk) <= set(digits.encode("ascii"))))
3659         with self.assertRaises(DecodeError):
3660             GeneralizedTime().decode(
3661                 GeneralizedTime.tag_default +
3662                 len_encode(len(junk)) +
3663                 junk
3664             )
3665
3666     @given(
3667         binary(
3668             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3669             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3670         ),
3671         binary(min_size=1, max_size=1),
3672         binary(
3673             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3674             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3675         ),
3676     )
3677     def test_junk_dm(self, part0, part1, part2):
3678         junk = part0 + part1 + part2
3679         assume(not (set(junk) <= set(digits.encode("ascii"))))
3680         with self.assertRaises(DecodeError):
3681             GeneralizedTime().decode(
3682                 GeneralizedTime.tag_default +
3683                 len_encode(len(junk)) +
3684                 junk
3685             )
3686
3687
3688 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
3689     base_klass = UTCTime
3690     omit_ms = True
3691     min_datetime = datetime(2000, 1, 1)
3692     max_datetime = datetime(2049, 12, 31)
3693
3694     def test_go_vectors_invalid(self):
3695         for data in ((
3696                 b"a10506234540Z",
3697                 b"91a506234540Z",
3698                 b"9105a6234540Z",
3699                 b"910506a34540Z",
3700                 b"910506334a40Z",
3701                 b"91050633444aZ",
3702                 b"910506334461Z",
3703                 b"910506334400Za",
3704                 b"000100000000Z",
3705                 b"101302030405Z",
3706                 b"100002030405Z",
3707                 b"100100030405Z",
3708                 b"100132030405Z",
3709                 b"100231030405Z",
3710                 b"100102240405Z",
3711                 b"100102036005Z",
3712                 b"100102030460Z",
3713                 b"-100102030410Z",
3714                 b"10-0102030410Z",
3715                 b"10-0002030410Z",
3716                 b"1001-02030410Z",
3717                 b"100102-030410Z",
3718                 b"10010203-0410Z",
3719                 b"1001020304-10Z",
3720                 # These ones are INVALID in *DER*, but accepted
3721                 # by Go's encoding/asn1
3722                 b"910506164540-0700",
3723                 b"910506164540+0730",
3724                 b"9105062345Z",
3725                 b"5105062345Z",
3726         )):
3727             with self.assertRaises(DecodeError) as err:
3728                 UTCTime(data)
3729             repr(err.exception)
3730
3731     def test_go_vectors_valid(self):
3732         self.assertEqual(
3733             UTCTime(b"910506234540Z").todatetime(),
3734             datetime(1991, 5, 6, 23, 45, 40, 0),
3735         )
3736
3737     @given(integers(min_value=0, max_value=49))
3738     def test_pre50(self, year):
3739         self.assertEqual(
3740             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3741             2000 + year,
3742         )
3743
3744     @given(integers(min_value=50, max_value=99))
3745     def test_post50(self, year):
3746         self.assertEqual(
3747             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3748             1900 + year,
3749         )
3750
3751     @given(
3752         binary(
3753             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
3754             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
3755         ),
3756         binary(min_size=1, max_size=1),
3757         binary(
3758             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
3759             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
3760         ),
3761     )
3762     def test_junk(self, part0, part1, part2):
3763         junk = part0 + part1 + part2
3764         assume(not (set(junk) <= set(digits.encode("ascii"))))
3765         with self.assertRaises(DecodeError):
3766             UTCTime().decode(
3767                 UTCTime.tag_default +
3768                 len_encode(len(junk)) +
3769                 junk
3770             )
3771
3772
3773 @composite
3774 def any_values_strategy(draw, do_expl=False):
3775     value = draw(one_of(none(), binary()))
3776     expl = None
3777     if do_expl:
3778         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3779     optional = draw(one_of(none(), booleans()))
3780     _decoded = (
3781         draw(integers(min_value=0)),
3782         draw(integers(min_value=0)),
3783         draw(integers(min_value=0)),
3784     )
3785     return (value, expl, optional, _decoded)
3786
3787
3788 class AnyInherited(Any):
3789     pass
3790
3791
3792 class TestAny(CommonMixin, TestCase):
3793     base_klass = Any
3794
3795     def test_invalid_value_type(self):
3796         with self.assertRaises(InvalidValueType) as err:
3797             Any(123)
3798         repr(err.exception)
3799
3800     @given(booleans())
3801     def test_optional(self, optional):
3802         obj = Any(optional=optional)
3803         self.assertEqual(obj.optional, optional)
3804
3805     @given(binary())
3806     def test_ready(self, value):
3807         obj = Any()
3808         self.assertFalse(obj.ready)
3809         repr(obj)
3810         pprint(obj)
3811         with self.assertRaises(ObjNotReady) as err:
3812             obj.encode()
3813         repr(err.exception)
3814         obj = Any(value)
3815         self.assertTrue(obj.ready)
3816         repr(obj)
3817         pprint(obj)
3818
3819     @given(integers())
3820     def test_basic(self, value):
3821         integer_encoded = Integer(value).encode()
3822         for obj in (
3823                 Any(integer_encoded),
3824                 Any(Integer(value)),
3825                 Any(Any(Integer(value))),
3826         ):
3827             self.assertSequenceEqual(bytes(obj), integer_encoded)
3828             self.assertEqual(
3829                 obj.decode(obj.encode())[0].vlen,
3830                 len(integer_encoded),
3831             )
3832             repr(obj)
3833             pprint(obj)
3834             self.assertSequenceEqual(obj.encode(), integer_encoded)
3835
3836     @given(binary(), binary())
3837     def test_comparison(self, value1, value2):
3838         for klass in (Any, AnyInherited):
3839             obj1 = klass(value1)
3840             obj2 = klass(value2)
3841             self.assertEqual(obj1 == obj2, value1 == value2)
3842             self.assertEqual(obj1 != obj2, value1 != value2)
3843             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3844
3845     @given(data_strategy())
3846     def test_call(self, d):
3847         for klass in (Any, AnyInherited):
3848             (
3849                 value_initial,
3850                 expl_initial,
3851                 optional_initial,
3852                 _decoded_initial,
3853             ) = d.draw(any_values_strategy())
3854             obj_initial = klass(
3855                 value_initial,
3856                 expl_initial,
3857                 optional_initial or False,
3858                 _decoded_initial,
3859             )
3860             (
3861                 value,
3862                 expl,
3863                 optional,
3864                 _decoded,
3865             ) = d.draw(any_values_strategy(do_expl=True))
3866             obj = obj_initial(value, expl, optional)
3867             if obj.ready:
3868                 value_expected = None if value is None else value
3869                 self.assertEqual(obj, value_expected)
3870             self.assertEqual(obj.expl_tag, expl or expl_initial)
3871             if obj.default is None:
3872                 optional = optional_initial if optional is None else optional
3873                 optional = False if optional is None else optional
3874             self.assertEqual(obj.optional, optional)
3875
3876     def test_simultaneous_impl_expl(self):
3877         # override it, as Any does not have implicit tag
3878         pass
3879
3880     def test_decoded(self):
3881         # override it, as Any does not have implicit tag
3882         pass
3883
3884     @given(any_values_strategy())
3885     def test_copy(self, values):
3886         for klass in (Any, AnyInherited):
3887             obj = klass(*values)
3888             obj_copied = obj.copy()
3889             self.assert_copied_basic_fields(obj, obj_copied)
3890             self.assertEqual(obj._value, obj_copied._value)
3891
3892     @given(binary().map(OctetString))
3893     def test_stripped(self, value):
3894         obj = Any(value)
3895         with self.assertRaises(NotEnoughData):
3896             obj.decode(obj.encode()[:-1])
3897
3898     @given(
3899         binary(),
3900         integers(min_value=1).map(tag_ctxc),
3901     )
3902     def test_stripped_expl(self, value, tag_expl):
3903         obj = Any(value, expl=tag_expl)
3904         with self.assertRaises(NotEnoughData):
3905             obj.decode(obj.encode()[:-1])
3906
3907     @given(
3908         integers(min_value=31),
3909         integers(min_value=0),
3910         decode_path_strat,
3911     )
3912     def test_bad_tag(self, tag, offset, decode_path):
3913         with self.assertRaises(DecodeError) as err:
3914             Any().decode(
3915                 tag_encode(tag)[:-1],
3916                 offset=offset,
3917                 decode_path=decode_path,
3918             )
3919         repr(err.exception)
3920         self.assertEqual(err.exception.offset, offset)
3921         self.assertEqual(err.exception.decode_path, decode_path)
3922
3923     @given(
3924         integers(min_value=128),
3925         integers(min_value=0),
3926         decode_path_strat,
3927     )
3928     def test_bad_len(self, l, offset, decode_path):
3929         with self.assertRaises(DecodeError) as err:
3930             Any().decode(
3931                 Any.tag_default + len_encode(l)[:-1],
3932                 offset=offset,
3933                 decode_path=decode_path,
3934             )
3935         repr(err.exception)
3936         self.assertEqual(err.exception.offset, offset)
3937         self.assertEqual(err.exception.decode_path, decode_path)
3938
3939     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3940     @given(
3941         any_values_strategy(),
3942         integers().map(lambda x: Integer(x).encode()),
3943         integers(min_value=1).map(tag_ctxc),
3944         integers(min_value=0),
3945         binary(max_size=5),
3946     )
3947     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
3948         for klass in (Any, AnyInherited):
3949             _, _, optional, _decoded = values
3950             obj = klass(value=value, optional=optional, _decoded=_decoded)
3951             repr(obj)
3952             pprint(obj)
3953             self.assertFalse(obj.expled)
3954             obj_encoded = obj.encode()
3955             obj_expled = obj(value, expl=tag_expl)
3956             self.assertTrue(obj_expled.expled)
3957             repr(obj_expled)
3958             pprint(obj_expled)
3959             obj_expled_encoded = obj_expled.encode()
3960             obj_decoded, tail = obj_expled.decode(
3961                 obj_expled_encoded + tail_junk,
3962                 offset=offset,
3963             )
3964             repr(obj_decoded)
3965             pprint(obj_decoded)
3966             self.assertEqual(tail, tail_junk)
3967             self.assertEqual(obj_decoded, obj_expled)
3968             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3969             self.assertEqual(bytes(obj_decoded), bytes(obj))
3970             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3971             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3972             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3973             self.assertEqual(
3974                 obj_decoded.expl_llen,
3975                 len(len_encode(len(obj_encoded))),
3976             )
3977             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3978             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3979             self.assertEqual(
3980                 obj_decoded.offset,
3981                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3982             )
3983             self.assertEqual(obj_decoded.expl_offset, offset)
3984             self.assertEqual(obj_decoded.tlen, 0)
3985             self.assertEqual(obj_decoded.llen, 0)
3986             self.assertEqual(obj_decoded.vlen, len(value))
3987
3988     @given(
3989         integers(min_value=1).map(tag_ctxc),
3990         integers(min_value=0, max_value=3),
3991         integers(min_value=0),
3992         decode_path_strat,
3993         binary(),
3994     )
3995     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
3996         chunk = Boolean(False, expl=expl).encode()
3997         encoded = (
3998             OctetString.tag_default +
3999             LENINDEF +
4000             b"".join([chunk] * chunks) +
4001             EOC
4002         )
4003         obj, tail = Any().decode(
4004             encoded + junk,
4005             offset=offset,
4006             decode_path=decode_path,
4007             ctx={"bered": True},
4008         )
4009         self.assertSequenceEqual(tail, junk)
4010         self.assertEqual(obj.offset, offset)
4011         self.assertEqual(obj.tlvlen, len(encoded))
4012         with self.assertRaises(NotEnoughData) as err:
4013             Any().decode(
4014                 encoded[:-1],
4015                 offset=offset,
4016                 decode_path=decode_path,
4017                 ctx={"bered": True},
4018             )
4019         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
4020         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
4021
4022
4023 @composite
4024 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
4025     if schema is None:
4026         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
4027         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
4028             one_of(
4029                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
4030                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
4031             ),
4032             min_size=len(names),
4033             max_size=len(names),
4034         ))]
4035         schema = [
4036             (name, Integer(**tag_kwargs))
4037             for name, tag_kwargs in zip(names, tags)
4038         ]
4039     value = None
4040     if value_required or draw(booleans()):
4041         value = draw(tuples(
4042             sampled_from([name for name, _ in schema]),
4043             integers().map(Integer),
4044         ))
4045     expl = None
4046     if do_expl:
4047         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4048     default = draw(one_of(
4049         none(),
4050         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
4051     ))
4052     optional = draw(one_of(none(), booleans()))
4053     _decoded = (
4054         draw(integers(min_value=0)),
4055         draw(integers(min_value=0)),
4056         draw(integers(min_value=0)),
4057     )
4058     return (schema, value, expl, default, optional, _decoded)
4059
4060
4061 class ChoiceInherited(Choice):
4062     pass
4063
4064
4065 class TestChoice(CommonMixin, TestCase):
4066     class Wahl(Choice):
4067         schema = (("whatever", Boolean()),)
4068     base_klass = Wahl
4069
4070     def test_schema_required(self):
4071         with assertRaisesRegex(self, ValueError, "schema must be specified"):
4072             Choice()
4073
4074     def test_impl_forbidden(self):
4075         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
4076             Choice(impl=b"whatever")
4077
4078     def test_invalid_value_type(self):
4079         with self.assertRaises(InvalidValueType) as err:
4080             self.base_klass(123)
4081         repr(err.exception)
4082         with self.assertRaises(ObjUnknown) as err:
4083             self.base_klass(("whenever", Boolean(False)))
4084         repr(err.exception)
4085         with self.assertRaises(InvalidValueType) as err:
4086             self.base_klass(("whatever", Integer(123)))
4087         repr(err.exception)
4088
4089     @given(booleans())
4090     def test_optional(self, optional):
4091         obj = self.base_klass(
4092             default=self.base_klass(("whatever", Boolean(False))),
4093             optional=optional,
4094         )
4095         self.assertTrue(obj.optional)
4096
4097     @given(booleans())
4098     def test_ready(self, value):
4099         obj = self.base_klass()
4100         self.assertFalse(obj.ready)
4101         repr(obj)
4102         pprint(obj)
4103         self.assertIsNone(obj["whatever"])
4104         with self.assertRaises(ObjNotReady) as err:
4105             obj.encode()
4106         repr(err.exception)
4107         obj["whatever"] = Boolean()
4108         self.assertFalse(obj.ready)
4109         repr(obj)
4110         pprint(obj)
4111         obj["whatever"] = Boolean(value)
4112         self.assertTrue(obj.ready)
4113         repr(obj)
4114         pprint(obj)
4115
4116     @given(booleans(), booleans())
4117     def test_comparison(self, value1, value2):
4118         class WahlInherited(self.base_klass):
4119             pass
4120         for klass in (self.base_klass, WahlInherited):
4121             obj1 = klass(("whatever", Boolean(value1)))
4122             obj2 = klass(("whatever", Boolean(value2)))
4123             self.assertEqual(obj1 == obj2, value1 == value2)
4124             self.assertEqual(obj1 != obj2, value1 != value2)
4125             self.assertEqual(obj1 == obj2._value, value1 == value2)
4126             self.assertFalse(obj1 == obj2._value[1])
4127
4128     @given(data_strategy())
4129     def test_call(self, d):
4130         for klass in (Choice, ChoiceInherited):
4131             (
4132                 schema_initial,
4133                 value_initial,
4134                 expl_initial,
4135                 default_initial,
4136                 optional_initial,
4137                 _decoded_initial,
4138             ) = d.draw(choice_values_strategy())
4139
4140             class Wahl(klass):
4141                 schema = schema_initial
4142             obj_initial = Wahl(
4143                 value=value_initial,
4144                 expl=expl_initial,
4145                 default=default_initial,
4146                 optional=optional_initial or False,
4147                 _decoded=_decoded_initial,
4148             )
4149             (
4150                 _,
4151                 value,
4152                 expl,
4153                 default,
4154                 optional,
4155                 _decoded,
4156             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
4157             obj = obj_initial(value, expl, default, optional)
4158             if obj.ready:
4159                 value_expected = default if value is None else value
4160                 value_expected = (
4161                     default_initial if value_expected is None
4162                     else value_expected
4163                 )
4164                 self.assertEqual(obj.choice, value_expected[0])
4165                 self.assertEqual(obj.value, int(value_expected[1]))
4166             self.assertEqual(obj.expl_tag, expl or expl_initial)
4167             default_expect = default_initial if default is None else default
4168             if default_expect is not None:
4169                 self.assertEqual(obj.default.choice, default_expect[0])
4170                 self.assertEqual(obj.default.value, int(default_expect[1]))
4171             if obj.default is None:
4172                 optional = optional_initial if optional is None else optional
4173                 optional = False if optional is None else optional
4174             else:
4175                 optional = True
4176             self.assertEqual(obj.optional, optional)
4177             self.assertEqual(obj.specs, obj_initial.specs)
4178
4179     def test_simultaneous_impl_expl(self):
4180         # override it, as Any does not have implicit tag
4181         pass
4182
4183     def test_decoded(self):
4184         # override it, as Any does not have implicit tag
4185         pass
4186
4187     @given(choice_values_strategy())
4188     def test_copy(self, values):
4189         _schema, value, expl, default, optional, _decoded = values
4190
4191         class Wahl(self.base_klass):
4192             schema = _schema
4193         obj = Wahl(
4194             value=value,
4195             expl=expl,
4196             default=default,
4197             optional=optional or False,
4198             _decoded=_decoded,
4199         )
4200         obj_copied = obj.copy()
4201         self.assertIsNone(obj.tag)
4202         self.assertIsNone(obj_copied.tag)
4203         # hack for assert_copied_basic_fields
4204         obj.tag = "whatever"
4205         obj_copied.tag = "whatever"
4206         self.assert_copied_basic_fields(obj, obj_copied)
4207         self.assertEqual(obj._value, obj_copied._value)
4208         self.assertEqual(obj.specs, obj_copied.specs)
4209
4210     @given(booleans())
4211     def test_stripped(self, value):
4212         obj = self.base_klass(("whatever", Boolean(value)))
4213         with self.assertRaises(NotEnoughData):
4214             obj.decode(obj.encode()[:-1])
4215
4216     @given(
4217         booleans(),
4218         integers(min_value=1).map(tag_ctxc),
4219     )
4220     def test_stripped_expl(self, value, tag_expl):
4221         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
4222         with self.assertRaises(NotEnoughData):
4223             obj.decode(obj.encode()[:-1])
4224
4225     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4226     @given(data_strategy())
4227     def test_symmetric(self, d):
4228         _schema, value, _, default, optional, _decoded = d.draw(
4229             choice_values_strategy(value_required=True)
4230         )
4231         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4232         offset = d.draw(integers(min_value=0))
4233         tail_junk = d.draw(binary(max_size=5))
4234
4235         class Wahl(self.base_klass):
4236             schema = _schema
4237         obj = Wahl(
4238             value=value,
4239             default=default,
4240             optional=optional,
4241             _decoded=_decoded,
4242         )
4243         repr(obj)
4244         pprint(obj)
4245         self.assertFalse(obj.expled)
4246         obj_encoded = obj.encode()
4247         obj_expled = obj(value, expl=tag_expl)
4248         self.assertTrue(obj_expled.expled)
4249         repr(obj_expled)
4250         pprint(obj_expled)
4251         obj_expled_encoded = obj_expled.encode()
4252         obj_decoded, tail = obj_expled.decode(
4253             obj_expled_encoded + tail_junk,
4254             offset=offset,
4255         )
4256         repr(obj_decoded)
4257         pprint(obj_decoded)
4258         self.assertEqual(tail, tail_junk)
4259         self.assertEqual(obj_decoded, obj_expled)
4260         self.assertEqual(obj_decoded.choice, obj_expled.choice)
4261         self.assertEqual(obj_decoded.value, obj_expled.value)
4262         self.assertEqual(obj_decoded.choice, obj.choice)
4263         self.assertEqual(obj_decoded.value, obj.value)
4264         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4265         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4266         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4267         self.assertEqual(
4268             obj_decoded.expl_llen,
4269             len(len_encode(len(obj_encoded))),
4270         )
4271         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4272         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4273         self.assertEqual(
4274             obj_decoded.offset,
4275             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4276         )
4277         self.assertEqual(obj_decoded.expl_offset, offset)
4278         self.assertSequenceEqual(
4279             obj_expled_encoded[
4280                 obj_decoded.value.fulloffset - offset:
4281                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
4282             ],
4283             obj_encoded,
4284         )
4285
4286     @given(integers())
4287     def test_set_get(self, value):
4288         class Wahl(Choice):
4289             schema = (
4290                 ("erste", Boolean()),
4291                 ("zweite", Integer()),
4292             )
4293         obj = Wahl()
4294         with self.assertRaises(ObjUnknown) as err:
4295             obj["whatever"] = "whenever"
4296         with self.assertRaises(InvalidValueType) as err:
4297             obj["zweite"] = Boolean(False)
4298         obj["zweite"] = Integer(value)
4299         repr(err.exception)
4300         with self.assertRaises(ObjUnknown) as err:
4301             obj["whatever"]
4302         repr(err.exception)
4303         self.assertIsNone(obj["erste"])
4304         self.assertEqual(obj["zweite"], Integer(value))
4305
4306     def test_tag_mismatch(self):
4307         class Wahl(Choice):
4308             schema = (
4309                 ("erste", Boolean()),
4310             )
4311         int_encoded = Integer(123).encode()
4312         bool_encoded = Boolean(False).encode()
4313         obj = Wahl()
4314         obj.decode(bool_encoded)
4315         with self.assertRaises(TagMismatch):
4316             obj.decode(int_encoded)
4317
4318     def test_tag_mismatch_underlying(self):
4319         class SeqOfBoolean(SequenceOf):
4320             schema = Boolean()
4321
4322         class SeqOfInteger(SequenceOf):
4323             schema = Integer()
4324
4325         class Wahl(Choice):
4326             schema = (
4327                 ("erste", SeqOfBoolean()),
4328             )
4329
4330         int_encoded = SeqOfInteger((Integer(123),)).encode()
4331         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
4332         obj = Wahl()
4333         obj.decode(bool_encoded)
4334         with self.assertRaises(TagMismatch) as err:
4335             obj.decode(int_encoded)
4336         self.assertEqual(err.exception.decode_path, ("erste", "0"))
4337
4338
4339 @composite
4340 def seq_values_strategy(draw, seq_klass, do_expl=False):
4341     value = None
4342     if draw(booleans()):
4343         value = seq_klass()
4344         value._value = {
4345             k: v for k, v in draw(dictionaries(
4346                 integers(),
4347                 one_of(
4348                     booleans().map(Boolean),
4349                     integers().map(Integer),
4350                 ),
4351             )).items()
4352         }
4353     schema = None
4354     if draw(booleans()):
4355         schema = list(draw(dictionaries(
4356             integers(),
4357             one_of(
4358                 booleans().map(Boolean),
4359                 integers().map(Integer),
4360             ),
4361         )).items())
4362     impl = None
4363     expl = None
4364     if do_expl:
4365         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4366     else:
4367         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4368     default = None
4369     if draw(booleans()):
4370         default = seq_klass()
4371         default._value = {
4372             k: v for k, v in draw(dictionaries(
4373                 integers(),
4374                 one_of(
4375                     booleans().map(Boolean),
4376                     integers().map(Integer),
4377                 ),
4378             )).items()
4379         }
4380     optional = draw(one_of(none(), booleans()))
4381     _decoded = (
4382         draw(integers(min_value=0)),
4383         draw(integers(min_value=0)),
4384         draw(integers(min_value=0)),
4385     )
4386     return (value, schema, impl, expl, default, optional, _decoded)
4387
4388
4389 @composite
4390 def sequence_strategy(draw, seq_klass):
4391     inputs = draw(lists(
4392         one_of(
4393             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
4394             tuples(just(Integer), integers(), one_of(none(), integers())),
4395         ),
4396         max_size=6,
4397     ))
4398     tags = draw(sets(
4399         integers(min_value=1),
4400         min_size=len(inputs),
4401         max_size=len(inputs),
4402     ))
4403     inits = [
4404         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4405         for tag, expled in zip(tags, draw(lists(
4406             booleans(),
4407             min_size=len(inputs),
4408             max_size=len(inputs),
4409         )))
4410     ]
4411     empties = []
4412     for i, optional in enumerate(draw(lists(
4413             sampled_from(("required", "optional", "empty")),
4414             min_size=len(inputs),
4415             max_size=len(inputs),
4416     ))):
4417         if optional in ("optional", "empty"):
4418             inits[i]["optional"] = True
4419         if optional == "empty":
4420             empties.append(i)
4421     empties = set(empties)
4422     names = list(draw(sets(
4423         text_printable,
4424         min_size=len(inputs),
4425         max_size=len(inputs),
4426     )))
4427     schema = []
4428     for i, (klass, value, default) in enumerate(inputs):
4429         schema.append((names[i], klass(default=default, **inits[i])))
4430     seq_name = draw(text_letters())
4431     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4432     seq = Seq()
4433     expects = []
4434     for i, (klass, value, default) in enumerate(inputs):
4435         name = names[i]
4436         _, spec = schema[i]
4437         expect = {
4438             "name": name,
4439             "optional": False,
4440             "presented": False,
4441             "default_value": None if spec.default is None else default,
4442             "value": None,
4443         }
4444         if i in empties:
4445             expect["optional"] = True
4446         else:
4447             expect["presented"] = True
4448             expect["value"] = value
4449             if spec.optional:
4450                 expect["optional"] = True
4451             if default is not None and default == value:
4452                 expect["presented"] = False
4453             seq[name] = klass(value)
4454         expects.append(expect)
4455     return seq, expects
4456
4457
4458 @composite
4459 def sequences_strategy(draw, seq_klass):
4460     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
4461     inits = [
4462         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4463         for tag, expled in zip(tags, draw(lists(
4464             booleans(),
4465             min_size=len(tags),
4466             max_size=len(tags),
4467         )))
4468     ]
4469     defaulted = set(
4470         i for i, is_default in enumerate(draw(lists(
4471             booleans(),
4472             min_size=len(tags),
4473             max_size=len(tags),
4474         ))) if is_default
4475     )
4476     names = list(draw(sets(
4477         text_printable,
4478         min_size=len(tags),
4479         max_size=len(tags),
4480     )))
4481     seq_expectses = draw(lists(
4482         sequence_strategy(seq_klass=seq_klass),
4483         min_size=len(tags),
4484         max_size=len(tags),
4485     ))
4486     seqs = [seq for seq, _ in seq_expectses]
4487     schema = []
4488     for i, (name, seq) in enumerate(zip(names, seqs)):
4489         schema.append((
4490             name,
4491             seq(default=(seq if i in defaulted else None), **inits[i]),
4492         ))
4493     seq_name = draw(text_letters())
4494     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4495     seq_outer = Seq()
4496     expect_outers = []
4497     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
4498         expect = {
4499             "name": name,
4500             "expects": expects_inner,
4501             "presented": False,
4502         }
4503         seq_outer[name] = seq_inner
4504         if seq_outer.specs[name].default is None:
4505             expect["presented"] = True
4506         expect_outers.append(expect)
4507     return seq_outer, expect_outers
4508
4509
4510 class SeqMixing(object):
4511     def test_invalid_value_type(self):
4512         with self.assertRaises(InvalidValueType) as err:
4513             self.base_klass(123)
4514         repr(err.exception)
4515
4516     def test_invalid_value_type_set(self):
4517         class Seq(self.base_klass):
4518             schema = (("whatever", Boolean()),)
4519         seq = Seq()
4520         with self.assertRaises(InvalidValueType) as err:
4521             seq["whatever"] = Integer(123)
4522         repr(err.exception)
4523
4524     @given(booleans())
4525     def test_optional(self, optional):
4526         obj = self.base_klass(default=self.base_klass(), optional=optional)
4527         self.assertTrue(obj.optional)
4528
4529     @given(data_strategy())
4530     def test_ready(self, d):
4531         ready = {
4532             str(i): v for i, v in enumerate(d.draw(lists(
4533                 booleans(),
4534                 min_size=1,
4535                 max_size=3,
4536             )))
4537         }
4538         non_ready = {
4539             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
4540                 booleans(),
4541                 min_size=1,
4542                 max_size=3,
4543             )))
4544         }
4545         schema_input = []
4546         for name in d.draw(permutations(
4547                 list(ready.keys()) + list(non_ready.keys()),
4548         )):
4549             schema_input.append((name, Boolean()))
4550
4551         class Seq(self.base_klass):
4552             schema = tuple(schema_input)
4553         seq = Seq()
4554         for name in ready.keys():
4555             seq[name]
4556             seq[name] = Boolean()
4557         self.assertFalse(seq.ready)
4558         repr(seq)
4559         pprint(seq)
4560         for name, value in ready.items():
4561             seq[name] = Boolean(value)
4562         self.assertFalse(seq.ready)
4563         repr(seq)
4564         pprint(seq)
4565         with self.assertRaises(ObjNotReady) as err:
4566             seq.encode()
4567         repr(err.exception)
4568         for name, value in non_ready.items():
4569             seq[name] = Boolean(value)
4570         self.assertTrue(seq.ready)
4571         repr(seq)
4572         pprint(seq)
4573
4574     @given(data_strategy())
4575     def test_call(self, d):
4576         class SeqInherited(self.base_klass):
4577             pass
4578         for klass in (self.base_klass, SeqInherited):
4579             (
4580                 value_initial,
4581                 schema_initial,
4582                 impl_initial,
4583                 expl_initial,
4584                 default_initial,
4585                 optional_initial,
4586                 _decoded_initial,
4587             ) = d.draw(seq_values_strategy(seq_klass=klass))
4588             obj_initial = klass(
4589                 value_initial,
4590                 schema_initial,
4591                 impl_initial,
4592                 expl_initial,
4593                 default_initial,
4594                 optional_initial or False,
4595                 _decoded_initial,
4596             )
4597             (
4598                 value,
4599                 _,
4600                 impl,
4601                 expl,
4602                 default,
4603                 optional,
4604                 _decoded,
4605             ) = d.draw(seq_values_strategy(
4606                 seq_klass=klass,
4607                 do_expl=impl_initial is None,
4608             ))
4609             obj = obj_initial(value, impl, expl, default, optional)
4610             value_expected = default if value is None else value
4611             value_expected = (
4612                 default_initial if value_expected is None
4613                 else value_expected
4614             )
4615             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
4616             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4617             self.assertEqual(obj.expl_tag, expl or expl_initial)
4618             self.assertEqual(
4619                 {} if obj.default is None else obj.default._value,
4620                 getattr(default_initial if default is None else default, "_value", {}),
4621             )
4622             if obj.default is None:
4623                 optional = optional_initial if optional is None else optional
4624                 optional = False if optional is None else optional
4625             else:
4626                 optional = True
4627             self.assertEqual(list(obj.specs.items()), schema_initial or [])
4628             self.assertEqual(obj.optional, optional)
4629
4630     @given(data_strategy())
4631     def test_copy(self, d):
4632         class SeqInherited(self.base_klass):
4633             pass
4634         for klass in (self.base_klass, SeqInherited):
4635             values = d.draw(seq_values_strategy(seq_klass=klass))
4636             obj = klass(*values)
4637             obj_copied = obj.copy()
4638             self.assert_copied_basic_fields(obj, obj_copied)
4639             self.assertEqual(obj.specs, obj_copied.specs)
4640             self.assertEqual(obj._value, obj_copied._value)
4641
4642     @given(data_strategy())
4643     def test_stripped(self, d):
4644         value = d.draw(integers())
4645         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4646
4647         class Seq(self.base_klass):
4648             impl = tag_impl
4649             schema = (("whatever", Integer()),)
4650         seq = Seq()
4651         seq["whatever"] = Integer(value)
4652         with self.assertRaises(NotEnoughData):
4653             seq.decode(seq.encode()[:-1])
4654
4655     @given(data_strategy())
4656     def test_stripped_expl(self, d):
4657         value = d.draw(integers())
4658         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4659
4660         class Seq(self.base_klass):
4661             expl = tag_expl
4662             schema = (("whatever", Integer()),)
4663         seq = Seq()
4664         seq["whatever"] = Integer(value)
4665         with self.assertRaises(NotEnoughData):
4666             seq.decode(seq.encode()[:-1])
4667
4668     @given(binary(min_size=2))
4669     def test_non_tag_mismatch_raised(self, junk):
4670         try:
4671             _, _, len_encoded = tag_strip(memoryview(junk))
4672             len_decode(len_encoded)
4673         except Exception:
4674             assume(True)
4675         else:
4676             assume(False)
4677
4678         class Seq(self.base_klass):
4679             schema = (
4680                 ("whatever", Integer()),
4681                 ("junk", Any()),
4682                 ("whenever", Integer()),
4683             )
4684         seq = Seq()
4685         seq["whatever"] = Integer(123)
4686         seq["junk"] = Any(junk)
4687         seq["whenever"] = Integer(123)
4688         with self.assertRaises(DecodeError):
4689             seq.decode(seq.encode())
4690
4691     @given(
4692         integers(min_value=31),
4693         integers(min_value=0),
4694         decode_path_strat,
4695     )
4696     def test_bad_tag(self, tag, offset, decode_path):
4697         with self.assertRaises(DecodeError) as err:
4698             self.base_klass().decode(
4699                 tag_encode(tag)[:-1],
4700                 offset=offset,
4701                 decode_path=decode_path,
4702             )
4703         repr(err.exception)
4704         self.assertEqual(err.exception.offset, offset)
4705         self.assertEqual(err.exception.decode_path, decode_path)
4706
4707     @given(
4708         integers(min_value=128),
4709         integers(min_value=0),
4710         decode_path_strat,
4711     )
4712     def test_bad_len(self, l, offset, decode_path):
4713         with self.assertRaises(DecodeError) as err:
4714             self.base_klass().decode(
4715                 self.base_klass.tag_default + len_encode(l)[:-1],
4716                 offset=offset,
4717                 decode_path=decode_path,
4718             )
4719         repr(err.exception)
4720         self.assertEqual(err.exception.offset, offset)
4721         self.assertEqual(err.exception.decode_path, decode_path)
4722
4723     def _assert_expects(self, seq, expects):
4724         for expect in expects:
4725             self.assertEqual(
4726                 seq.specs[expect["name"]].optional,
4727                 expect["optional"],
4728             )
4729             if expect["default_value"] is not None:
4730                 self.assertEqual(
4731                     seq.specs[expect["name"]].default,
4732                     expect["default_value"],
4733                 )
4734             if expect["presented"]:
4735                 self.assertIn(expect["name"], seq)
4736                 self.assertEqual(seq[expect["name"]], expect["value"])
4737
4738     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4739     @given(data_strategy())
4740     def test_symmetric(self, d):
4741         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
4742         tail_junk = d.draw(binary(max_size=5))
4743         self.assertTrue(seq.ready)
4744         self.assertFalse(seq.decoded)
4745         self._assert_expects(seq, expects)
4746         repr(seq)
4747         pprint(seq)
4748         self.assertTrue(seq.ready)
4749         seq_encoded = seq.encode()
4750         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
4751         self.assertFalse(seq_decoded.lenindef)
4752
4753         t, _, lv = tag_strip(seq_encoded)
4754         _, _, v = len_decode(lv)
4755         seq_encoded_lenindef = t + LENINDEF + v + EOC
4756         seq_decoded_lenindef, tail_lenindef = seq.decode(
4757             seq_encoded_lenindef + tail_junk,
4758             ctx={"bered": True},
4759         )
4760         self.assertTrue(seq_decoded_lenindef.lenindef)
4761         with self.assertRaises(DecodeError):
4762             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
4763         with self.assertRaises(DecodeError):
4764             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
4765         repr(seq_decoded_lenindef)
4766         pprint(seq_decoded_lenindef)
4767         self.assertTrue(seq_decoded_lenindef.ready)
4768
4769         for decoded, decoded_tail, encoded in (
4770                 (seq_decoded, tail, seq_encoded),
4771                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
4772         ):
4773             self.assertEqual(decoded_tail, tail_junk)
4774             self._assert_expects(decoded, expects)
4775             self.assertEqual(seq, decoded)
4776             self.assertEqual(decoded.encode(), seq_encoded)
4777             self.assertEqual(decoded.tlvlen, len(encoded))
4778             for expect in expects:
4779                 if not expect["presented"]:
4780                     self.assertNotIn(expect["name"], decoded)
4781                     continue
4782                 self.assertIn(expect["name"], decoded)
4783                 obj = decoded[expect["name"]]
4784                 self.assertTrue(obj.decoded)
4785                 offset = obj.expl_offset if obj.expled else obj.offset
4786                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4787                 self.assertSequenceEqual(
4788                     seq_encoded[offset:offset + tlvlen],
4789                     obj.encode(),
4790                 )
4791
4792     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4793     @given(data_strategy())
4794     def test_symmetric_with_seq(self, d):
4795         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
4796         self.assertTrue(seq.ready)
4797         seq_encoded = seq.encode()
4798         seq_decoded, tail = seq.decode(seq_encoded)
4799         self.assertEqual(tail, b"")
4800         self.assertTrue(seq.ready)
4801         self.assertEqual(seq, seq_decoded)
4802         self.assertEqual(seq_decoded.encode(), seq_encoded)
4803         for expect_outer in expect_outers:
4804             if not expect_outer["presented"]:
4805                 self.assertNotIn(expect_outer["name"], seq_decoded)
4806                 continue
4807             self.assertIn(expect_outer["name"], seq_decoded)
4808             obj = seq_decoded[expect_outer["name"]]
4809             self.assertTrue(obj.decoded)
4810             offset = obj.expl_offset if obj.expled else obj.offset
4811             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4812             self.assertSequenceEqual(
4813                 seq_encoded[offset:offset + tlvlen],
4814                 obj.encode(),
4815             )
4816             self._assert_expects(obj, expect_outer["expects"])
4817
4818     @given(data_strategy())
4819     def test_default_disappears(self, d):
4820         _schema = list(d.draw(dictionaries(
4821             text_letters(),
4822             sets(integers(), min_size=2, max_size=2),
4823             min_size=1,
4824         )).items())
4825
4826         class Seq(self.base_klass):
4827             schema = [
4828                 (n, Integer(default=d))
4829                 for n, (_, d) in _schema
4830             ]
4831         seq = Seq()
4832         for name, (value, _) in _schema:
4833             seq[name] = Integer(value)
4834         self.assertEqual(len(seq._value), len(_schema))
4835         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
4836         self.assertGreater(len(seq.encode()), len(empty_seq))
4837         for name, (_, default) in _schema:
4838             seq[name] = Integer(default)
4839         self.assertEqual(len(seq._value), 0)
4840         self.assertSequenceEqual(seq.encode(), empty_seq)
4841
4842     @given(data_strategy())
4843     def test_encoded_default_accepted(self, d):
4844         _schema = list(d.draw(dictionaries(
4845             text_letters(),
4846             integers(),
4847             min_size=1,
4848         )).items())
4849         tags = [tag_encode(tag) for tag in d.draw(sets(
4850             integers(min_value=0),
4851             min_size=len(_schema),
4852             max_size=len(_schema),
4853         ))]
4854
4855         class SeqWithoutDefault(self.base_klass):
4856             schema = [
4857                 (n, Integer(impl=t))
4858                 for (n, _), t in zip(_schema, tags)
4859             ]
4860         seq_without_default = SeqWithoutDefault()
4861         for name, value in _schema:
4862             seq_without_default[name] = Integer(value)
4863         seq_encoded = seq_without_default.encode()
4864
4865         class SeqWithDefault(self.base_klass):
4866             schema = [
4867                 (n, Integer(default=v, impl=t))
4868                 for (n, v), t in zip(_schema, tags)
4869             ]
4870         seq_with_default = SeqWithDefault()
4871         seq_decoded, _ = seq_with_default.decode(seq_encoded)
4872         for name, value in _schema:
4873             self.assertEqual(seq_decoded[name], seq_with_default[name])
4874             self.assertEqual(seq_decoded[name], value)
4875
4876     @given(data_strategy())
4877     def test_missing_from_spec(self, d):
4878         names = list(d.draw(sets(text_letters(), min_size=2)))
4879         tags = [tag_encode(tag) for tag in d.draw(sets(
4880             integers(min_value=0),
4881             min_size=len(names),
4882             max_size=len(names),
4883         ))]
4884         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
4885
4886         class SeqFull(self.base_klass):
4887             schema = [(n, Integer(impl=t)) for n, t in names_tags]
4888         seq_full = SeqFull()
4889         for i, name in enumerate(names):
4890             seq_full[name] = Integer(i)
4891         seq_encoded = seq_full.encode()
4892         altered = names_tags[:-2] + names_tags[-1:]
4893
4894         class SeqMissing(self.base_klass):
4895             schema = [(n, Integer(impl=t)) for n, t in altered]
4896         seq_missing = SeqMissing()
4897         with self.assertRaises(TagMismatch):
4898             seq_missing.decode(seq_encoded)
4899
4900
4901 class TestSequence(SeqMixing, CommonMixin, TestCase):
4902     base_klass = Sequence
4903
4904     @given(
4905         integers(),
4906         binary(min_size=1),
4907     )
4908     def test_remaining(self, value, junk):
4909         class Seq(Sequence):
4910             schema = (
4911                 ("whatever", Integer()),
4912             )
4913         int_encoded = Integer(value).encode()
4914         junked = b"".join((
4915             Sequence.tag_default,
4916             len_encode(len(int_encoded + junk)),
4917             int_encoded + junk,
4918         ))
4919         with assertRaisesRegex(self, DecodeError, "remaining"):
4920             Seq().decode(junked)
4921
4922     @given(sets(text_letters(), min_size=2))
4923     def test_obj_unknown(self, names):
4924         missing = names.pop()
4925
4926         class Seq(Sequence):
4927             schema = [(n, Boolean()) for n in names]
4928         seq = Seq()
4929         with self.assertRaises(ObjUnknown) as err:
4930             seq[missing]
4931         repr(err.exception)
4932         with self.assertRaises(ObjUnknown) as err:
4933             seq[missing] = Boolean()
4934         repr(err.exception)
4935
4936     def test_x690_vector(self):
4937         class Seq(Sequence):
4938             schema = (
4939                 ("name", IA5String()),
4940                 ("ok", Boolean()),
4941             )
4942         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
4943         self.assertEqual(seq["name"], "Smith")
4944         self.assertEqual(seq["ok"], True)
4945
4946
4947 class TestSet(SeqMixing, CommonMixin, TestCase):
4948     base_klass = Set
4949
4950     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4951     @given(data_strategy())
4952     def test_sorted(self, d):
4953         tags = [
4954             tag_encode(tag) for tag in
4955             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
4956         ]
4957
4958         class Seq(Set):
4959             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
4960         seq = Seq()
4961         for name, _ in Seq.schema:
4962             seq[name] = OctetString(b"")
4963         seq_encoded = seq.encode()
4964         seq_decoded, _ = seq.decode(seq_encoded)
4965         self.assertSequenceEqual(
4966             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
4967             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
4968         )
4969
4970
4971 @composite
4972 def seqof_values_strategy(draw, schema=None, do_expl=False):
4973     if schema is None:
4974         schema = draw(sampled_from((Boolean(), Integer())))
4975     bound_min, bound_max = sorted(draw(sets(
4976         integers(min_value=0, max_value=10),
4977         min_size=2,
4978         max_size=2,
4979     )))
4980     if isinstance(schema, Boolean):
4981         values_generator = booleans().map(Boolean)
4982     elif isinstance(schema, Integer):
4983         values_generator = integers().map(Integer)
4984     values_generator = lists(
4985         values_generator,
4986         min_size=bound_min,
4987         max_size=bound_max,
4988     )
4989     values = draw(one_of(none(), values_generator))
4990     impl = None
4991     expl = None
4992     if do_expl:
4993         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4994     else:
4995         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4996     default = draw(one_of(none(), values_generator))
4997     optional = draw(one_of(none(), booleans()))
4998     _decoded = (
4999         draw(integers(min_value=0)),
5000         draw(integers(min_value=0)),
5001         draw(integers(min_value=0)),
5002     )
5003     return (
5004         schema,
5005         values,
5006         (bound_min, bound_max),
5007         impl,
5008         expl,
5009         default,
5010         optional,
5011         _decoded,
5012     )
5013
5014
5015 class SeqOfMixing(object):
5016     def test_invalid_value_type(self):
5017         with self.assertRaises(InvalidValueType) as err:
5018             self.base_klass(123)
5019         repr(err.exception)
5020
5021     def test_invalid_values_type(self):
5022         class SeqOf(self.base_klass):
5023             schema = Integer()
5024         with self.assertRaises(InvalidValueType) as err:
5025             SeqOf([Integer(123), Boolean(False), Integer(234)])
5026         repr(err.exception)
5027
5028     def test_schema_required(self):
5029         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5030             self.base_klass.__mro__[1]()
5031
5032     @given(booleans(), booleans(), binary(), binary())
5033     def test_comparison(self, value1, value2, tag1, tag2):
5034         class SeqOf(self.base_klass):
5035             schema = Boolean()
5036         obj1 = SeqOf([Boolean(value1)])
5037         obj2 = SeqOf([Boolean(value2)])
5038         self.assertEqual(obj1 == obj2, value1 == value2)
5039         self.assertEqual(obj1 != obj2, value1 != value2)
5040         self.assertEqual(obj1 == list(obj2), value1 == value2)
5041         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
5042         obj1 = SeqOf([Boolean(value1)], impl=tag1)
5043         obj2 = SeqOf([Boolean(value1)], impl=tag2)
5044         self.assertEqual(obj1 == obj2, tag1 == tag2)
5045         self.assertEqual(obj1 != obj2, tag1 != tag2)
5046
5047     @given(lists(booleans()))
5048     def test_iter(self, values):
5049         class SeqOf(self.base_klass):
5050             schema = Boolean()
5051         obj = SeqOf([Boolean(value) for value in values])
5052         self.assertEqual(len(obj), len(values))
5053         for i, value in enumerate(obj):
5054             self.assertEqual(value, values[i])
5055
5056     @given(data_strategy())
5057     def test_ready(self, d):
5058         ready = [Integer(v) for v in d.draw(lists(
5059             integers(),
5060             min_size=1,
5061             max_size=3,
5062         ))]
5063         non_ready = [
5064             Integer() for _ in
5065             range(d.draw(integers(min_value=1, max_value=5)))
5066         ]
5067
5068         class SeqOf(self.base_klass):
5069             schema = Integer()
5070         values = d.draw(permutations(ready + non_ready))
5071         seqof = SeqOf()
5072         for value in values:
5073             seqof.append(value)
5074         self.assertFalse(seqof.ready)
5075         repr(seqof)
5076         pprint(seqof)
5077         with self.assertRaises(ObjNotReady) as err:
5078             seqof.encode()
5079         repr(err.exception)
5080         for i, value in enumerate(values):
5081             self.assertEqual(seqof[i], value)
5082             if not seqof[i].ready:
5083                 seqof[i] = Integer(i)
5084         self.assertTrue(seqof.ready)
5085         repr(seqof)
5086         pprint(seqof)
5087
5088     def test_spec_mismatch(self):
5089         class SeqOf(self.base_klass):
5090             schema = Integer()
5091         seqof = SeqOf()
5092         seqof.append(Integer(123))
5093         with self.assertRaises(ValueError):
5094             seqof.append(Boolean(False))
5095         with self.assertRaises(ValueError):
5096             seqof[0] = Boolean(False)
5097
5098     @given(data_strategy())
5099     def test_bounds_satisfied(self, d):
5100         class SeqOf(self.base_klass):
5101             schema = Boolean()
5102         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
5103         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5104         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
5105         SeqOf(value=value, bounds=(bound_min, bound_max))
5106
5107     @given(data_strategy())
5108     def test_bounds_unsatisfied(self, d):
5109         class SeqOf(self.base_klass):
5110             schema = Boolean()
5111         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
5112         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5113         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
5114         with self.assertRaises(BoundsError) as err:
5115             SeqOf(value=value, bounds=(bound_min, bound_max))
5116         repr(err.exception)
5117         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5118             SeqOf(bounds=(bound_min, bound_max)).decode(
5119                 SeqOf(value).encode()
5120             )
5121         repr(err.exception)
5122         value = [Boolean(True)] * d.draw(integers(
5123             min_value=bound_max + 1,
5124             max_value=bound_max + 10,
5125         ))
5126         with self.assertRaises(BoundsError) as err:
5127             SeqOf(value=value, bounds=(bound_min, bound_max))
5128         repr(err.exception)
5129         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5130             SeqOf(bounds=(bound_min, bound_max)).decode(
5131                 SeqOf(value).encode()
5132             )
5133         repr(err.exception)
5134
5135     @given(integers(min_value=1, max_value=10))
5136     def test_out_of_bounds(self, bound_max):
5137         class SeqOf(self.base_klass):
5138             schema = Integer()
5139             bounds = (0, bound_max)
5140         seqof = SeqOf()
5141         for _ in range(bound_max):
5142             seqof.append(Integer(123))
5143         with self.assertRaises(BoundsError):
5144             seqof.append(Integer(123))
5145
5146     @given(data_strategy())
5147     def test_call(self, d):
5148         (
5149             schema_initial,
5150             value_initial,
5151             bounds_initial,
5152             impl_initial,
5153             expl_initial,
5154             default_initial,
5155             optional_initial,
5156             _decoded_initial,
5157         ) = d.draw(seqof_values_strategy())
5158
5159         class SeqOf(self.base_klass):
5160             schema = schema_initial
5161         obj_initial = SeqOf(
5162             value=value_initial,
5163             bounds=bounds_initial,
5164             impl=impl_initial,
5165             expl=expl_initial,
5166             default=default_initial,
5167             optional=optional_initial or False,
5168             _decoded=_decoded_initial,
5169         )
5170         (
5171             _,
5172             value,
5173             bounds,
5174             impl,
5175             expl,
5176             default,
5177             optional,
5178             _decoded,
5179         ) = d.draw(seqof_values_strategy(
5180             schema=schema_initial,
5181             do_expl=impl_initial is None,
5182         ))
5183         if (default is None) and (obj_initial.default is not None):
5184             bounds = None
5185         if (
5186                 (bounds is None) and
5187                 (value is not None) and
5188                 (bounds_initial is not None) and
5189                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
5190         ):
5191             value = None
5192         if (
5193                 (bounds is None) and
5194                 (default is not None) and
5195                 (bounds_initial is not None) and
5196                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
5197         ):
5198             default = None
5199         obj = obj_initial(
5200             value=value,
5201             bounds=bounds,
5202             impl=impl,
5203             expl=expl,
5204             default=default,
5205             optional=optional,
5206         )
5207         if obj.ready:
5208             value_expected = default if value is None else value
5209             value_expected = (
5210                 default_initial if value_expected is None
5211                 else value_expected
5212             )
5213             value_expected = () if value_expected is None else value_expected
5214             self.assertEqual(obj, value_expected)
5215         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5216         self.assertEqual(obj.expl_tag, expl or expl_initial)
5217         self.assertEqual(
5218             obj.default,
5219             default_initial if default is None else default,
5220         )
5221         if obj.default is None:
5222             optional = optional_initial if optional is None else optional
5223             optional = False if optional is None else optional
5224         else:
5225             optional = True
5226         self.assertEqual(obj.optional, optional)
5227         self.assertEqual(
5228             (obj._bound_min, obj._bound_max),
5229             bounds or bounds_initial or (0, float("+inf")),
5230         )
5231
5232     @given(seqof_values_strategy())
5233     def test_copy(self, values):
5234         _schema, value, bounds, impl, expl, default, optional, _decoded = values
5235
5236         class SeqOf(self.base_klass):
5237             schema = _schema
5238         obj = SeqOf(
5239             value=value,
5240             bounds=bounds,
5241             impl=impl,
5242             expl=expl,
5243             default=default,
5244             optional=optional or False,
5245             _decoded=_decoded,
5246         )
5247         obj_copied = obj.copy()
5248         self.assert_copied_basic_fields(obj, obj_copied)
5249         self.assertEqual(obj._bound_min, obj_copied._bound_min)
5250         self.assertEqual(obj._bound_max, obj_copied._bound_max)
5251         self.assertEqual(obj._value, obj_copied._value)
5252
5253     @given(
5254         lists(binary()),
5255         integers(min_value=1).map(tag_encode),
5256     )
5257     def test_stripped(self, values, tag_impl):
5258         class SeqOf(self.base_klass):
5259             schema = OctetString()
5260         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
5261         with self.assertRaises(NotEnoughData):
5262             obj.decode(obj.encode()[:-1])
5263
5264     @given(
5265         lists(binary()),
5266         integers(min_value=1).map(tag_ctxc),
5267     )
5268     def test_stripped_expl(self, values, tag_expl):
5269         class SeqOf(self.base_klass):
5270             schema = OctetString()
5271         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
5272         with self.assertRaises(NotEnoughData):
5273             obj.decode(obj.encode()[:-1])
5274
5275     @given(
5276         integers(min_value=31),
5277         integers(min_value=0),
5278         decode_path_strat,
5279     )
5280     def test_bad_tag(self, tag, offset, decode_path):
5281         with self.assertRaises(DecodeError) as err:
5282             self.base_klass().decode(
5283                 tag_encode(tag)[:-1],
5284                 offset=offset,
5285                 decode_path=decode_path,
5286             )
5287         repr(err.exception)
5288         self.assertEqual(err.exception.offset, offset)
5289         self.assertEqual(err.exception.decode_path, decode_path)
5290
5291     @given(
5292         integers(min_value=128),
5293         integers(min_value=0),
5294         decode_path_strat,
5295     )
5296     def test_bad_len(self, l, offset, decode_path):
5297         with self.assertRaises(DecodeError) as err:
5298             self.base_klass().decode(
5299                 self.base_klass.tag_default + len_encode(l)[:-1],
5300                 offset=offset,
5301                 decode_path=decode_path,
5302             )
5303         repr(err.exception)
5304         self.assertEqual(err.exception.offset, offset)
5305         self.assertEqual(err.exception.decode_path, decode_path)
5306
5307     @given(binary(min_size=1))
5308     def test_tag_mismatch(self, impl):
5309         assume(impl != self.base_klass.tag_default)
5310         with self.assertRaises(TagMismatch):
5311             self.base_klass(impl=impl).decode(self.base_klass().encode())
5312
5313     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5314     @given(
5315         seqof_values_strategy(schema=Integer()),
5316         lists(integers().map(Integer)),
5317         integers(min_value=1).map(tag_ctxc),
5318         integers(min_value=0),
5319         binary(max_size=5),
5320     )
5321     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
5322         _, _, _, _, _, default, optional, _decoded = values
5323
5324         class SeqOf(self.base_klass):
5325             schema = Integer()
5326         obj = SeqOf(
5327             value=value,
5328             default=default,
5329             optional=optional,
5330             _decoded=_decoded,
5331         )
5332         repr(obj)
5333         pprint(obj)
5334         self.assertFalse(obj.expled)
5335         obj_encoded = obj.encode()
5336         obj_expled = obj(value, expl=tag_expl)
5337         self.assertTrue(obj_expled.expled)
5338         repr(obj_expled)
5339         pprint(obj_expled)
5340         obj_expled_encoded = obj_expled.encode()
5341         obj_decoded, tail = obj_expled.decode(
5342             obj_expled_encoded + tail_junk,
5343             offset=offset,
5344         )
5345         repr(obj_decoded)
5346         pprint(obj_decoded)
5347         self.assertEqual(tail, tail_junk)
5348         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
5349         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5350         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5351         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5352         self.assertEqual(
5353             obj_decoded.expl_llen,
5354             len(len_encode(len(obj_encoded))),
5355         )
5356         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5357         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5358         self.assertEqual(
5359             obj_decoded.offset,
5360             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5361         )
5362         self.assertEqual(obj_decoded.expl_offset, offset)
5363         for obj_inner in obj_decoded:
5364             self.assertIn(obj_inner, obj_decoded)
5365             self.assertSequenceEqual(
5366                 obj_inner.encode(),
5367                 obj_expled_encoded[
5368                     obj_inner.offset - offset:
5369                     obj_inner.offset + obj_inner.tlvlen - offset
5370                 ],
5371             )
5372
5373         t, _, lv = tag_strip(obj_encoded)
5374         _, _, v = len_decode(lv)
5375         obj_encoded_lenindef = t + LENINDEF + v + EOC
5376         obj_decoded_lenindef, tail_lenindef = obj.decode(
5377             obj_encoded_lenindef + tail_junk,
5378             ctx={"bered": True},
5379         )
5380         self.assertTrue(obj_decoded_lenindef.lenindef)
5381         repr(obj_decoded_lenindef)
5382         pprint(obj_decoded_lenindef)
5383         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
5384         with self.assertRaises(DecodeError):
5385             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
5386         with self.assertRaises(DecodeError):
5387             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
5388
5389
5390 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
5391     class SeqOf(SequenceOf):
5392         schema = "whatever"
5393     base_klass = SeqOf
5394
5395     def _test_symmetric_compare_objs(self, obj1, obj2):
5396         self.assertEqual(obj1, obj2)
5397         self.assertSequenceEqual(list(obj1), list(obj2))
5398
5399
5400 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
5401     class SeqOf(SetOf):
5402         schema = "whatever"
5403     base_klass = SeqOf
5404
5405     def _test_symmetric_compare_objs(self, obj1, obj2):
5406         self.assertSetEqual(
5407             set(int(v) for v in obj1),
5408             set(int(v) for v in obj2),
5409         )
5410
5411     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5412     @given(data_strategy())
5413     def test_sorted(self, d):
5414         values = [OctetString(v) for v in d.draw(lists(binary()))]
5415
5416         class Seq(SetOf):
5417             schema = OctetString()
5418         seq = Seq(values)
5419         seq_encoded = seq.encode()
5420         seq_decoded, _ = seq.decode(seq_encoded)
5421         self.assertSequenceEqual(
5422             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5423             b"".join(sorted([v.encode() for v in values])),
5424         )
5425
5426
5427 class TestGoMarshalVectors(TestCase):
5428     def runTest(self):
5429         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
5430         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
5431         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
5432         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
5433         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
5434
5435         class Seq(Sequence):
5436             schema = (
5437                 ("erste", Integer()),
5438                 ("zweite", Integer(optional=True))
5439             )
5440         seq = Seq()
5441         seq["erste"] = Integer(64)
5442         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5443         seq["erste"] = Integer(0x123456)
5444         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
5445         seq["erste"] = Integer(64)
5446         seq["zweite"] = Integer(65)
5447         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
5448
5449         class NestedSeq(Sequence):
5450             schema = (
5451                 ("nest", Seq()),
5452             )
5453         seq["erste"] = Integer(127)
5454         seq["zweite"] = None
5455         nested = NestedSeq()
5456         nested["nest"] = seq
5457         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
5458
5459         self.assertSequenceEqual(
5460             OctetString(b"\x01\x02\x03").encode(),
5461             hexdec("0403010203"),
5462         )
5463
5464         class Seq(Sequence):
5465             schema = (
5466                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
5467             )
5468         seq = Seq()
5469         seq["erste"] = Integer(64)
5470         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
5471
5472         class Seq(Sequence):
5473             schema = (
5474                 ("erste", Integer(expl=tag_ctxc(5))),
5475             )
5476         seq = Seq()
5477         seq["erste"] = Integer(64)
5478         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
5479
5480         class Seq(Sequence):
5481             schema = (
5482                 ("erste", Null(
5483                     impl=tag_encode(0, klass=TagClassContext),
5484                     optional=True,
5485                 )),
5486             )
5487         seq = Seq()
5488         seq["erste"] = Null()
5489         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
5490         seq["erste"] = None
5491         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5492
5493         self.assertSequenceEqual(
5494             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
5495             hexdec("170d3730303130313030303030305a"),
5496         )
5497         self.assertSequenceEqual(
5498             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
5499             hexdec("170d3039313131353232353631365a"),
5500         )
5501         self.assertSequenceEqual(
5502             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
5503             hexdec("180f32313030303430353132303130315a"),
5504         )
5505
5506         class Seq(Sequence):
5507             schema = (
5508                 ("erste", GeneralizedTime()),
5509             )
5510         seq = Seq()
5511         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
5512         self.assertSequenceEqual(
5513             seq.encode(),
5514             hexdec("3011180f32303039313131353232353631365a"),
5515         )
5516
5517         self.assertSequenceEqual(
5518             BitString((1, b"\x80")).encode(),
5519             hexdec("03020780"),
5520         )
5521         self.assertSequenceEqual(
5522             BitString((12, b"\x81\xF0")).encode(),
5523             hexdec("03030481f0"),
5524         )
5525
5526         self.assertSequenceEqual(
5527             ObjectIdentifier("1.2.3.4").encode(),
5528             hexdec("06032a0304"),
5529         )
5530         self.assertSequenceEqual(
5531             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
5532             hexdec("06092a864888932d010105"),
5533         )
5534         self.assertSequenceEqual(
5535             ObjectIdentifier("2.100.3").encode(),
5536             hexdec("0603813403"),
5537         )
5538
5539         self.assertSequenceEqual(
5540             PrintableString("test").encode(),
5541             hexdec("130474657374"),
5542         )
5543         self.assertSequenceEqual(
5544             PrintableString("x" * 127).encode(),
5545             hexdec("137F" + "78" * 127),
5546         )
5547         self.assertSequenceEqual(
5548             PrintableString("x" * 128).encode(),
5549             hexdec("138180" + "78" * 128),
5550         )
5551         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
5552
5553         class Seq(Sequence):
5554             schema = (
5555                 ("erste", IA5String()),
5556             )
5557         seq = Seq()
5558         seq["erste"] = IA5String("test")
5559         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
5560
5561         class Seq(Sequence):
5562             schema = (
5563                 ("erste", PrintableString()),
5564             )
5565         seq = Seq()
5566         seq["erste"] = PrintableString("test")
5567         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
5568         seq["erste"] = PrintableString("test*")
5569         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
5570
5571         class Seq(Sequence):
5572             schema = (
5573                 ("erste", Any(optional=True)),
5574                 ("zweite", Integer()),
5575             )
5576         seq = Seq()
5577         seq["zweite"] = Integer(64)
5578         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5579
5580         class Seq(SetOf):
5581             schema = Integer()
5582         seq = Seq()
5583         seq.append(Integer(10))
5584         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
5585
5586         class _SeqOf(SequenceOf):
5587             schema = PrintableString()
5588
5589         class SeqOf(SequenceOf):
5590             schema = _SeqOf()
5591         _seqof = _SeqOf()
5592         _seqof.append(PrintableString("1"))
5593         seqof = SeqOf()
5594         seqof.append(_seqof)
5595         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
5596
5597         class Seq(Sequence):
5598             schema = (
5599                 ("erste", Integer(default=1)),
5600             )
5601         seq = Seq()
5602         seq["erste"] = Integer(0)
5603         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
5604         seq["erste"] = Integer(1)
5605         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5606         seq["erste"] = Integer(2)
5607         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
5608
5609
5610 class TestPP(TestCase):
5611     @given(data_strategy())
5612     def test_oid_printing(self, d):
5613         oids = {
5614             str(ObjectIdentifier(k)): v * 2
5615             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
5616         }
5617         chosen = d.draw(sampled_from(sorted(oids)))
5618         chosen_id = oids[chosen]
5619         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
5620         self.assertNotIn(chosen_id, pp_console_row(pp))
5621         self.assertIn(chosen_id, pp_console_row(pp, oids=oids))
5622
5623
5624 class TestAutoAddSlots(TestCase):
5625     def runTest(self):
5626         class Inher(Integer):
5627             pass
5628
5629         with self.assertRaises(AttributeError):
5630             inher = Inher()
5631             inher.unexistent = "whatever"
5632
5633
5634 class TestOIDDefines(TestCase):
5635     @given(data_strategy())
5636     def runTest(self, d):
5637         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
5638         value_name_chosen = d.draw(sampled_from(value_names))
5639         oids = [
5640             ObjectIdentifier(oid)
5641             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
5642         ]
5643         oid_chosen = d.draw(sampled_from(oids))
5644         values = d.draw(lists(
5645             integers(),
5646             min_size=len(value_names),
5647             max_size=len(value_names),
5648         ))
5649         _schema = [
5650             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
5651                 oid: Integer() for oid in oids[:-1]
5652             }),))),
5653         ]
5654         for i, value_name in enumerate(value_names):
5655             _schema.append((value_name, Any(expl=tag_ctxp(i))))
5656
5657         class Seq(Sequence):
5658             schema = _schema
5659         seq = Seq()
5660         for value_name, value in zip(value_names, values):
5661             seq[value_name] = Any(Integer(value).encode())
5662         seq["type"] = oid_chosen
5663         seq, _ = Seq().decode(seq.encode())
5664         for value_name in value_names:
5665             if value_name == value_name_chosen:
5666                 continue
5667             self.assertIsNone(seq[value_name].defined)
5668         if value_name_chosen in oids[:-1]:
5669             self.assertIsNotNone(seq[value_name_chosen].defined)
5670             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
5671             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
5672
5673
5674 class TestDefinesByPath(TestCase):
5675     def test_generated(self):
5676         class Seq(Sequence):
5677             schema = (
5678                 ("type", ObjectIdentifier()),
5679                 ("value", OctetString(expl=tag_ctxc(123))),
5680             )
5681
5682         class SeqInner(Sequence):
5683             schema = (
5684                 ("typeInner", ObjectIdentifier()),
5685                 ("valueInner", Any()),
5686             )
5687
5688         class PairValue(SetOf):
5689             schema = Any()
5690
5691         class Pair(Sequence):
5692             schema = (
5693                 ("type", ObjectIdentifier()),
5694                 ("value", PairValue()),
5695             )
5696
5697         class Pairs(SequenceOf):
5698             schema = Pair()
5699
5700         (
5701             type_integered,
5702             type_sequenced,
5703             type_innered,
5704             type_octet_stringed,
5705         ) = [
5706             ObjectIdentifier(oid)
5707             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
5708         ]
5709         seq_integered = Seq()
5710         seq_integered["type"] = type_integered
5711         seq_integered["value"] = OctetString(Integer(123).encode())
5712         seq_integered_raw = seq_integered.encode()
5713
5714         pairs = Pairs()
5715         pairs_input = (
5716             (type_octet_stringed, OctetString(b"whatever")),
5717             (type_integered, Integer(123)),
5718             (type_octet_stringed, OctetString(b"whenever")),
5719             (type_integered, Integer(234)),
5720         )
5721         for t, v in pairs_input:
5722             pair = Pair()
5723             pair["type"] = t
5724             pair["value"] = PairValue((Any(v),))
5725             pairs.append(pair)
5726         seq_inner = SeqInner()
5727         seq_inner["typeInner"] = type_innered
5728         seq_inner["valueInner"] = Any(pairs)
5729         seq_sequenced = Seq()
5730         seq_sequenced["type"] = type_sequenced
5731         seq_sequenced["value"] = OctetString(seq_inner.encode())
5732         seq_sequenced_raw = seq_sequenced.encode()
5733
5734         defines_by_path = []
5735         seq_integered, _ = Seq().decode(seq_integered_raw)
5736         self.assertIsNone(seq_integered["value"].defined)
5737         defines_by_path.append(
5738             (("type",), ((("value",), {
5739                 type_integered: Integer(),
5740                 type_sequenced: SeqInner(),
5741             }),))
5742         )
5743         seq_integered, _ = Seq().decode(
5744             seq_integered_raw,
5745             ctx={"defines_by_path": defines_by_path},
5746         )
5747         self.assertIsNotNone(seq_integered["value"].defined)
5748         self.assertEqual(seq_integered["value"].defined[0], type_integered)
5749         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
5750         self.assertTrue(seq_integered_raw[
5751             seq_integered["value"].defined[1].offset:
5752         ].startswith(Integer(123).encode()))
5753
5754         seq_sequenced, _ = Seq().decode(
5755             seq_sequenced_raw,
5756             ctx={"defines_by_path": defines_by_path},
5757         )
5758         self.assertIsNotNone(seq_sequenced["value"].defined)
5759         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5760         seq_inner = seq_sequenced["value"].defined[1]
5761         self.assertIsNone(seq_inner["valueInner"].defined)
5762
5763         defines_by_path.append((
5764             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
5765             ((("valueInner",), {type_innered: Pairs()}),),
5766         ))
5767         seq_sequenced, _ = Seq().decode(
5768             seq_sequenced_raw,
5769             ctx={"defines_by_path": defines_by_path},
5770         )
5771         self.assertIsNotNone(seq_sequenced["value"].defined)
5772         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5773         seq_inner = seq_sequenced["value"].defined[1]
5774         self.assertIsNotNone(seq_inner["valueInner"].defined)
5775         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5776         pairs = seq_inner["valueInner"].defined[1]
5777         for pair in pairs:
5778             self.assertIsNone(pair["value"][0].defined)
5779
5780         defines_by_path.append((
5781             (
5782                 "value",
5783                 DecodePathDefBy(type_sequenced),
5784                 "valueInner",
5785                 DecodePathDefBy(type_innered),
5786                 any,
5787                 "type",
5788             ),
5789             ((("value",), {
5790                 type_integered: Integer(),
5791                 type_octet_stringed: OctetString(),
5792             }),),
5793         ))
5794         seq_sequenced, _ = Seq().decode(
5795             seq_sequenced_raw,
5796             ctx={"defines_by_path": defines_by_path},
5797         )
5798         self.assertIsNotNone(seq_sequenced["value"].defined)
5799         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5800         seq_inner = seq_sequenced["value"].defined[1]
5801         self.assertIsNotNone(seq_inner["valueInner"].defined)
5802         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5803         pairs_got = seq_inner["valueInner"].defined[1]
5804         for pair_input, pair_got in zip(pairs_input, pairs_got):
5805             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
5806             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
5807
5808     @given(oid_strategy(), integers())
5809     def test_simple(self, oid, tgt):
5810         class Inner(Sequence):
5811             schema = (
5812                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
5813                     ObjectIdentifier(oid): Integer(),
5814                 }),))),
5815             )
5816
5817         class Outer(Sequence):
5818             schema = (
5819                 ("inner", Inner()),
5820                 ("tgt", OctetString()),
5821             )
5822
5823         inner = Inner()
5824         inner["oid"] = ObjectIdentifier(oid)
5825         outer = Outer()
5826         outer["inner"] = inner
5827         outer["tgt"] = OctetString(Integer(tgt).encode())
5828         decoded, _ = Outer().decode(outer.encode())
5829         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
5830
5831
5832 class TestAbsDecodePath(TestCase):
5833     @given(
5834         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5835         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5836     )
5837     def test_concat(self, decode_path, rel_path):
5838         self.assertSequenceEqual(
5839             abs_decode_path(decode_path, rel_path),
5840             decode_path + rel_path,
5841         )
5842
5843     @given(
5844         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5845         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5846     )
5847     def test_abs(self, decode_path, rel_path):
5848         self.assertSequenceEqual(
5849             abs_decode_path(decode_path, ("/",) + rel_path),
5850             rel_path,
5851         )
5852
5853     @given(
5854         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
5855         integers(min_value=1, max_value=3),
5856         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5857     )
5858     def test_dots(self, decode_path, number_of_dots, rel_path):
5859         self.assertSequenceEqual(
5860             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
5861             decode_path[:-number_of_dots] + rel_path,
5862         )
5863
5864
5865 class TestStrictDefaultExistence(TestCase):
5866     @given(data_strategy())
5867     def runTest(self, d):
5868         count = d.draw(integers(min_value=1, max_value=10))
5869         chosen = d.draw(integers(min_value=0, max_value=count - 1))
5870         _schema = [
5871             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
5872             for i in range(count)
5873         ]
5874
5875         class Seq(Sequence):
5876             schema = _schema
5877         seq = Seq()
5878         for i in range(count):
5879             seq["int%d" % i] = Integer(123)
5880         raw = seq.encode()
5881         chosen = "int%d" % chosen
5882         seq.specs[chosen] = seq.specs[chosen](default=123)
5883         seq.decode(raw)
5884         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5885             seq.decode(raw, ctx={"strict_default_existence": True})
5886
5887
5888 class TestX690PrefixedType(TestCase):
5889     def runTest(self):
5890         self.assertSequenceEqual(
5891             VisibleString("Jones").encode(),
5892             hexdec("1A054A6F6E6573"),
5893         )
5894         self.assertSequenceEqual(
5895             VisibleString(
5896                 "Jones",
5897                 impl=tag_encode(3, klass=TagClassApplication),
5898             ).encode(),
5899             hexdec("43054A6F6E6573"),
5900         )
5901         self.assertSequenceEqual(
5902             Any(
5903                 VisibleString(
5904                     "Jones",
5905                     impl=tag_encode(3, klass=TagClassApplication),
5906                 ),
5907                 expl=tag_ctxc(2),
5908             ).encode(),
5909             hexdec("A20743054A6F6E6573"),
5910         )
5911         self.assertSequenceEqual(
5912             OctetString(
5913                 VisibleString(
5914                     "Jones",
5915                     impl=tag_encode(3, klass=TagClassApplication),
5916                 ).encode(),
5917                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
5918             ).encode(),
5919             hexdec("670743054A6F6E6573"),
5920         )
5921         self.assertSequenceEqual(
5922             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
5923             hexdec("82054A6F6E6573"),
5924         )
5925
5926
5927 class TestExplOOB(TestCase):
5928     def runTest(self):
5929         expl = tag_ctxc(123)
5930         raw = Integer(123).encode() + Integer(234).encode()
5931         raw = b"".join((expl, len_encode(len(raw)), raw))
5932         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
5933             Integer(expl=expl).decode(raw)
5934         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})