]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Defaulted values are checked by default
[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         self.assertFalse(seq_decoded.bered)
4753
4754         t, _, lv = tag_strip(seq_encoded)
4755         _, _, v = len_decode(lv)
4756         seq_encoded_lenindef = t + LENINDEF + v + EOC
4757         seq_decoded_lenindef, tail_lenindef = seq.decode(
4758             seq_encoded_lenindef + tail_junk,
4759             ctx={"bered": True},
4760         )
4761         self.assertTrue(seq_decoded_lenindef.lenindef)
4762         with self.assertRaises(DecodeError):
4763             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
4764         with self.assertRaises(DecodeError):
4765             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
4766         repr(seq_decoded_lenindef)
4767         pprint(seq_decoded_lenindef)
4768         self.assertTrue(seq_decoded_lenindef.ready)
4769
4770         for decoded, decoded_tail, encoded in (
4771                 (seq_decoded, tail, seq_encoded),
4772                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
4773         ):
4774             self.assertEqual(decoded_tail, tail_junk)
4775             self._assert_expects(decoded, expects)
4776             self.assertEqual(seq, decoded)
4777             self.assertEqual(decoded.encode(), seq_encoded)
4778             self.assertEqual(decoded.tlvlen, len(encoded))
4779             for expect in expects:
4780                 if not expect["presented"]:
4781                     self.assertNotIn(expect["name"], decoded)
4782                     continue
4783                 self.assertIn(expect["name"], decoded)
4784                 obj = decoded[expect["name"]]
4785                 self.assertTrue(obj.decoded)
4786                 offset = obj.expl_offset if obj.expled else obj.offset
4787                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4788                 self.assertSequenceEqual(
4789                     seq_encoded[offset:offset + tlvlen],
4790                     obj.encode(),
4791                 )
4792
4793     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4794     @given(data_strategy())
4795     def test_symmetric_with_seq(self, d):
4796         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
4797         self.assertTrue(seq.ready)
4798         seq_encoded = seq.encode()
4799         seq_decoded, tail = seq.decode(seq_encoded)
4800         self.assertEqual(tail, b"")
4801         self.assertTrue(seq.ready)
4802         self.assertEqual(seq, seq_decoded)
4803         self.assertEqual(seq_decoded.encode(), seq_encoded)
4804         for expect_outer in expect_outers:
4805             if not expect_outer["presented"]:
4806                 self.assertNotIn(expect_outer["name"], seq_decoded)
4807                 continue
4808             self.assertIn(expect_outer["name"], seq_decoded)
4809             obj = seq_decoded[expect_outer["name"]]
4810             self.assertTrue(obj.decoded)
4811             offset = obj.expl_offset if obj.expled else obj.offset
4812             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4813             self.assertSequenceEqual(
4814                 seq_encoded[offset:offset + tlvlen],
4815                 obj.encode(),
4816             )
4817             self._assert_expects(obj, expect_outer["expects"])
4818
4819     @given(data_strategy())
4820     def test_default_disappears(self, d):
4821         _schema = list(d.draw(dictionaries(
4822             text_letters(),
4823             sets(integers(), min_size=2, max_size=2),
4824             min_size=1,
4825         )).items())
4826
4827         class Seq(self.base_klass):
4828             schema = [
4829                 (n, Integer(default=d))
4830                 for n, (_, d) in _schema
4831             ]
4832         seq = Seq()
4833         for name, (value, _) in _schema:
4834             seq[name] = Integer(value)
4835         self.assertEqual(len(seq._value), len(_schema))
4836         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
4837         self.assertGreater(len(seq.encode()), len(empty_seq))
4838         for name, (_, default) in _schema:
4839             seq[name] = Integer(default)
4840         self.assertEqual(len(seq._value), 0)
4841         self.assertSequenceEqual(seq.encode(), empty_seq)
4842
4843     @given(data_strategy())
4844     def test_encoded_default_not_accepted(self, d):
4845         _schema = list(d.draw(dictionaries(
4846             text_letters(),
4847             integers(),
4848             min_size=1,
4849         )).items())
4850         tags = [tag_encode(tag) for tag in d.draw(sets(
4851             integers(min_value=0),
4852             min_size=len(_schema),
4853             max_size=len(_schema),
4854         ))]
4855
4856         class SeqWithoutDefault(self.base_klass):
4857             schema = [
4858                 (n, Integer(impl=t))
4859                 for (n, _), t in zip(_schema, tags)
4860             ]
4861         seq_without_default = SeqWithoutDefault()
4862         for name, value in _schema:
4863             seq_without_default[name] = Integer(value)
4864         seq_encoded = seq_without_default.encode()
4865
4866         class SeqWithDefault(self.base_klass):
4867             schema = [
4868                 (n, Integer(default=v, impl=t))
4869                 for (n, v), t in zip(_schema, tags)
4870             ]
4871         seq_with_default = SeqWithDefault()
4872         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
4873             seq_with_default.decode(seq_encoded)
4874         for ctx in ({"bered": True}, {"allow_default_values": True}):
4875             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
4876             self.assertTrue(seq_decoded.bered)
4877             for name, value in _schema:
4878                 self.assertEqual(seq_decoded[name], seq_with_default[name])
4879                 self.assertEqual(seq_decoded[name], value)
4880
4881     @given(data_strategy())
4882     def test_missing_from_spec(self, d):
4883         names = list(d.draw(sets(text_letters(), min_size=2)))
4884         tags = [tag_encode(tag) for tag in d.draw(sets(
4885             integers(min_value=0),
4886             min_size=len(names),
4887             max_size=len(names),
4888         ))]
4889         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
4890
4891         class SeqFull(self.base_klass):
4892             schema = [(n, Integer(impl=t)) for n, t in names_tags]
4893         seq_full = SeqFull()
4894         for i, name in enumerate(names):
4895             seq_full[name] = Integer(i)
4896         seq_encoded = seq_full.encode()
4897         altered = names_tags[:-2] + names_tags[-1:]
4898
4899         class SeqMissing(self.base_klass):
4900             schema = [(n, Integer(impl=t)) for n, t in altered]
4901         seq_missing = SeqMissing()
4902         with self.assertRaises(TagMismatch):
4903             seq_missing.decode(seq_encoded)
4904
4905
4906 class TestSequence(SeqMixing, CommonMixin, TestCase):
4907     base_klass = Sequence
4908
4909     @given(
4910         integers(),
4911         binary(min_size=1),
4912     )
4913     def test_remaining(self, value, junk):
4914         class Seq(Sequence):
4915             schema = (
4916                 ("whatever", Integer()),
4917             )
4918         int_encoded = Integer(value).encode()
4919         junked = b"".join((
4920             Sequence.tag_default,
4921             len_encode(len(int_encoded + junk)),
4922             int_encoded + junk,
4923         ))
4924         with assertRaisesRegex(self, DecodeError, "remaining"):
4925             Seq().decode(junked)
4926
4927     @given(sets(text_letters(), min_size=2))
4928     def test_obj_unknown(self, names):
4929         missing = names.pop()
4930
4931         class Seq(Sequence):
4932             schema = [(n, Boolean()) for n in names]
4933         seq = Seq()
4934         with self.assertRaises(ObjUnknown) as err:
4935             seq[missing]
4936         repr(err.exception)
4937         with self.assertRaises(ObjUnknown) as err:
4938             seq[missing] = Boolean()
4939         repr(err.exception)
4940
4941     def test_x690_vector(self):
4942         class Seq(Sequence):
4943             schema = (
4944                 ("name", IA5String()),
4945                 ("ok", Boolean()),
4946             )
4947         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
4948         self.assertEqual(seq["name"], "Smith")
4949         self.assertEqual(seq["ok"], True)
4950
4951
4952 class TestSet(SeqMixing, CommonMixin, TestCase):
4953     base_klass = Set
4954
4955     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4956     @given(data_strategy())
4957     def test_sorted(self, d):
4958         tags = [
4959             tag_encode(tag) for tag in
4960             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
4961         ]
4962
4963         class Seq(Set):
4964             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
4965         seq = Seq()
4966         for name, _ in Seq.schema:
4967             seq[name] = OctetString(b"")
4968         seq_encoded = seq.encode()
4969         seq_decoded, _ = seq.decode(seq_encoded)
4970         self.assertSequenceEqual(
4971             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
4972             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
4973         )
4974
4975
4976 @composite
4977 def seqof_values_strategy(draw, schema=None, do_expl=False):
4978     if schema is None:
4979         schema = draw(sampled_from((Boolean(), Integer())))
4980     bound_min, bound_max = sorted(draw(sets(
4981         integers(min_value=0, max_value=10),
4982         min_size=2,
4983         max_size=2,
4984     )))
4985     if isinstance(schema, Boolean):
4986         values_generator = booleans().map(Boolean)
4987     elif isinstance(schema, Integer):
4988         values_generator = integers().map(Integer)
4989     values_generator = lists(
4990         values_generator,
4991         min_size=bound_min,
4992         max_size=bound_max,
4993     )
4994     values = draw(one_of(none(), values_generator))
4995     impl = None
4996     expl = None
4997     if do_expl:
4998         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4999     else:
5000         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5001     default = draw(one_of(none(), values_generator))
5002     optional = draw(one_of(none(), booleans()))
5003     _decoded = (
5004         draw(integers(min_value=0)),
5005         draw(integers(min_value=0)),
5006         draw(integers(min_value=0)),
5007     )
5008     return (
5009         schema,
5010         values,
5011         (bound_min, bound_max),
5012         impl,
5013         expl,
5014         default,
5015         optional,
5016         _decoded,
5017     )
5018
5019
5020 class SeqOfMixing(object):
5021     def test_invalid_value_type(self):
5022         with self.assertRaises(InvalidValueType) as err:
5023             self.base_klass(123)
5024         repr(err.exception)
5025
5026     def test_invalid_values_type(self):
5027         class SeqOf(self.base_klass):
5028             schema = Integer()
5029         with self.assertRaises(InvalidValueType) as err:
5030             SeqOf([Integer(123), Boolean(False), Integer(234)])
5031         repr(err.exception)
5032
5033     def test_schema_required(self):
5034         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5035             self.base_klass.__mro__[1]()
5036
5037     @given(booleans(), booleans(), binary(), binary())
5038     def test_comparison(self, value1, value2, tag1, tag2):
5039         class SeqOf(self.base_klass):
5040             schema = Boolean()
5041         obj1 = SeqOf([Boolean(value1)])
5042         obj2 = SeqOf([Boolean(value2)])
5043         self.assertEqual(obj1 == obj2, value1 == value2)
5044         self.assertEqual(obj1 != obj2, value1 != value2)
5045         self.assertEqual(obj1 == list(obj2), value1 == value2)
5046         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
5047         obj1 = SeqOf([Boolean(value1)], impl=tag1)
5048         obj2 = SeqOf([Boolean(value1)], impl=tag2)
5049         self.assertEqual(obj1 == obj2, tag1 == tag2)
5050         self.assertEqual(obj1 != obj2, tag1 != tag2)
5051
5052     @given(lists(booleans()))
5053     def test_iter(self, values):
5054         class SeqOf(self.base_klass):
5055             schema = Boolean()
5056         obj = SeqOf([Boolean(value) for value in values])
5057         self.assertEqual(len(obj), len(values))
5058         for i, value in enumerate(obj):
5059             self.assertEqual(value, values[i])
5060
5061     @given(data_strategy())
5062     def test_ready(self, d):
5063         ready = [Integer(v) for v in d.draw(lists(
5064             integers(),
5065             min_size=1,
5066             max_size=3,
5067         ))]
5068         non_ready = [
5069             Integer() for _ in
5070             range(d.draw(integers(min_value=1, max_value=5)))
5071         ]
5072
5073         class SeqOf(self.base_klass):
5074             schema = Integer()
5075         values = d.draw(permutations(ready + non_ready))
5076         seqof = SeqOf()
5077         for value in values:
5078             seqof.append(value)
5079         self.assertFalse(seqof.ready)
5080         repr(seqof)
5081         pprint(seqof)
5082         with self.assertRaises(ObjNotReady) as err:
5083             seqof.encode()
5084         repr(err.exception)
5085         for i, value in enumerate(values):
5086             self.assertEqual(seqof[i], value)
5087             if not seqof[i].ready:
5088                 seqof[i] = Integer(i)
5089         self.assertTrue(seqof.ready)
5090         repr(seqof)
5091         pprint(seqof)
5092
5093     def test_spec_mismatch(self):
5094         class SeqOf(self.base_klass):
5095             schema = Integer()
5096         seqof = SeqOf()
5097         seqof.append(Integer(123))
5098         with self.assertRaises(ValueError):
5099             seqof.append(Boolean(False))
5100         with self.assertRaises(ValueError):
5101             seqof[0] = Boolean(False)
5102
5103     @given(data_strategy())
5104     def test_bounds_satisfied(self, d):
5105         class SeqOf(self.base_klass):
5106             schema = Boolean()
5107         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
5108         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5109         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
5110         SeqOf(value=value, bounds=(bound_min, bound_max))
5111
5112     @given(data_strategy())
5113     def test_bounds_unsatisfied(self, d):
5114         class SeqOf(self.base_klass):
5115             schema = Boolean()
5116         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
5117         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5118         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
5119         with self.assertRaises(BoundsError) as err:
5120             SeqOf(value=value, bounds=(bound_min, bound_max))
5121         repr(err.exception)
5122         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5123             SeqOf(bounds=(bound_min, bound_max)).decode(
5124                 SeqOf(value).encode()
5125             )
5126         repr(err.exception)
5127         value = [Boolean(True)] * d.draw(integers(
5128             min_value=bound_max + 1,
5129             max_value=bound_max + 10,
5130         ))
5131         with self.assertRaises(BoundsError) as err:
5132             SeqOf(value=value, bounds=(bound_min, bound_max))
5133         repr(err.exception)
5134         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5135             SeqOf(bounds=(bound_min, bound_max)).decode(
5136                 SeqOf(value).encode()
5137             )
5138         repr(err.exception)
5139
5140     @given(integers(min_value=1, max_value=10))
5141     def test_out_of_bounds(self, bound_max):
5142         class SeqOf(self.base_klass):
5143             schema = Integer()
5144             bounds = (0, bound_max)
5145         seqof = SeqOf()
5146         for _ in range(bound_max):
5147             seqof.append(Integer(123))
5148         with self.assertRaises(BoundsError):
5149             seqof.append(Integer(123))
5150
5151     @given(data_strategy())
5152     def test_call(self, d):
5153         (
5154             schema_initial,
5155             value_initial,
5156             bounds_initial,
5157             impl_initial,
5158             expl_initial,
5159             default_initial,
5160             optional_initial,
5161             _decoded_initial,
5162         ) = d.draw(seqof_values_strategy())
5163
5164         class SeqOf(self.base_klass):
5165             schema = schema_initial
5166         obj_initial = SeqOf(
5167             value=value_initial,
5168             bounds=bounds_initial,
5169             impl=impl_initial,
5170             expl=expl_initial,
5171             default=default_initial,
5172             optional=optional_initial or False,
5173             _decoded=_decoded_initial,
5174         )
5175         (
5176             _,
5177             value,
5178             bounds,
5179             impl,
5180             expl,
5181             default,
5182             optional,
5183             _decoded,
5184         ) = d.draw(seqof_values_strategy(
5185             schema=schema_initial,
5186             do_expl=impl_initial is None,
5187         ))
5188         if (default is None) and (obj_initial.default is not None):
5189             bounds = None
5190         if (
5191                 (bounds is None) and
5192                 (value is not None) and
5193                 (bounds_initial is not None) and
5194                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
5195         ):
5196             value = None
5197         if (
5198                 (bounds is None) and
5199                 (default is not None) and
5200                 (bounds_initial is not None) and
5201                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
5202         ):
5203             default = None
5204         obj = obj_initial(
5205             value=value,
5206             bounds=bounds,
5207             impl=impl,
5208             expl=expl,
5209             default=default,
5210             optional=optional,
5211         )
5212         if obj.ready:
5213             value_expected = default if value is None else value
5214             value_expected = (
5215                 default_initial if value_expected is None
5216                 else value_expected
5217             )
5218             value_expected = () if value_expected is None else value_expected
5219             self.assertEqual(obj, value_expected)
5220         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5221         self.assertEqual(obj.expl_tag, expl or expl_initial)
5222         self.assertEqual(
5223             obj.default,
5224             default_initial if default is None else default,
5225         )
5226         if obj.default is None:
5227             optional = optional_initial if optional is None else optional
5228             optional = False if optional is None else optional
5229         else:
5230             optional = True
5231         self.assertEqual(obj.optional, optional)
5232         self.assertEqual(
5233             (obj._bound_min, obj._bound_max),
5234             bounds or bounds_initial or (0, float("+inf")),
5235         )
5236
5237     @given(seqof_values_strategy())
5238     def test_copy(self, values):
5239         _schema, value, bounds, impl, expl, default, optional, _decoded = values
5240
5241         class SeqOf(self.base_klass):
5242             schema = _schema
5243         obj = SeqOf(
5244             value=value,
5245             bounds=bounds,
5246             impl=impl,
5247             expl=expl,
5248             default=default,
5249             optional=optional or False,
5250             _decoded=_decoded,
5251         )
5252         obj_copied = obj.copy()
5253         self.assert_copied_basic_fields(obj, obj_copied)
5254         self.assertEqual(obj._bound_min, obj_copied._bound_min)
5255         self.assertEqual(obj._bound_max, obj_copied._bound_max)
5256         self.assertEqual(obj._value, obj_copied._value)
5257
5258     @given(
5259         lists(binary()),
5260         integers(min_value=1).map(tag_encode),
5261     )
5262     def test_stripped(self, values, tag_impl):
5263         class SeqOf(self.base_klass):
5264             schema = OctetString()
5265         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
5266         with self.assertRaises(NotEnoughData):
5267             obj.decode(obj.encode()[:-1])
5268
5269     @given(
5270         lists(binary()),
5271         integers(min_value=1).map(tag_ctxc),
5272     )
5273     def test_stripped_expl(self, values, tag_expl):
5274         class SeqOf(self.base_klass):
5275             schema = OctetString()
5276         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
5277         with self.assertRaises(NotEnoughData):
5278             obj.decode(obj.encode()[:-1])
5279
5280     @given(
5281         integers(min_value=31),
5282         integers(min_value=0),
5283         decode_path_strat,
5284     )
5285     def test_bad_tag(self, tag, offset, decode_path):
5286         with self.assertRaises(DecodeError) as err:
5287             self.base_klass().decode(
5288                 tag_encode(tag)[:-1],
5289                 offset=offset,
5290                 decode_path=decode_path,
5291             )
5292         repr(err.exception)
5293         self.assertEqual(err.exception.offset, offset)
5294         self.assertEqual(err.exception.decode_path, decode_path)
5295
5296     @given(
5297         integers(min_value=128),
5298         integers(min_value=0),
5299         decode_path_strat,
5300     )
5301     def test_bad_len(self, l, offset, decode_path):
5302         with self.assertRaises(DecodeError) as err:
5303             self.base_klass().decode(
5304                 self.base_klass.tag_default + len_encode(l)[:-1],
5305                 offset=offset,
5306                 decode_path=decode_path,
5307             )
5308         repr(err.exception)
5309         self.assertEqual(err.exception.offset, offset)
5310         self.assertEqual(err.exception.decode_path, decode_path)
5311
5312     @given(binary(min_size=1))
5313     def test_tag_mismatch(self, impl):
5314         assume(impl != self.base_klass.tag_default)
5315         with self.assertRaises(TagMismatch):
5316             self.base_klass(impl=impl).decode(self.base_klass().encode())
5317
5318     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5319     @given(
5320         seqof_values_strategy(schema=Integer()),
5321         lists(integers().map(Integer)),
5322         integers(min_value=1).map(tag_ctxc),
5323         integers(min_value=0),
5324         binary(max_size=5),
5325     )
5326     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
5327         _, _, _, _, _, default, optional, _decoded = values
5328
5329         class SeqOf(self.base_klass):
5330             schema = Integer()
5331         obj = SeqOf(
5332             value=value,
5333             default=default,
5334             optional=optional,
5335             _decoded=_decoded,
5336         )
5337         repr(obj)
5338         pprint(obj)
5339         self.assertFalse(obj.expled)
5340         obj_encoded = obj.encode()
5341         obj_expled = obj(value, expl=tag_expl)
5342         self.assertTrue(obj_expled.expled)
5343         repr(obj_expled)
5344         pprint(obj_expled)
5345         obj_expled_encoded = obj_expled.encode()
5346         obj_decoded, tail = obj_expled.decode(
5347             obj_expled_encoded + tail_junk,
5348             offset=offset,
5349         )
5350         repr(obj_decoded)
5351         pprint(obj_decoded)
5352         self.assertEqual(tail, tail_junk)
5353         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
5354         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5355         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5356         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5357         self.assertEqual(
5358             obj_decoded.expl_llen,
5359             len(len_encode(len(obj_encoded))),
5360         )
5361         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5362         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5363         self.assertEqual(
5364             obj_decoded.offset,
5365             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5366         )
5367         self.assertEqual(obj_decoded.expl_offset, offset)
5368         for obj_inner in obj_decoded:
5369             self.assertIn(obj_inner, obj_decoded)
5370             self.assertSequenceEqual(
5371                 obj_inner.encode(),
5372                 obj_expled_encoded[
5373                     obj_inner.offset - offset:
5374                     obj_inner.offset + obj_inner.tlvlen - offset
5375                 ],
5376             )
5377
5378         t, _, lv = tag_strip(obj_encoded)
5379         _, _, v = len_decode(lv)
5380         obj_encoded_lenindef = t + LENINDEF + v + EOC
5381         obj_decoded_lenindef, tail_lenindef = obj.decode(
5382             obj_encoded_lenindef + tail_junk,
5383             ctx={"bered": True},
5384         )
5385         self.assertTrue(obj_decoded_lenindef.lenindef)
5386         repr(obj_decoded_lenindef)
5387         pprint(obj_decoded_lenindef)
5388         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
5389         with self.assertRaises(DecodeError):
5390             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
5391         with self.assertRaises(DecodeError):
5392             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
5393
5394
5395 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
5396     class SeqOf(SequenceOf):
5397         schema = "whatever"
5398     base_klass = SeqOf
5399
5400     def _test_symmetric_compare_objs(self, obj1, obj2):
5401         self.assertEqual(obj1, obj2)
5402         self.assertSequenceEqual(list(obj1), list(obj2))
5403
5404
5405 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
5406     class SeqOf(SetOf):
5407         schema = "whatever"
5408     base_klass = SeqOf
5409
5410     def _test_symmetric_compare_objs(self, obj1, obj2):
5411         self.assertSetEqual(
5412             set(int(v) for v in obj1),
5413             set(int(v) for v in obj2),
5414         )
5415
5416     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5417     @given(data_strategy())
5418     def test_sorted(self, d):
5419         values = [OctetString(v) for v in d.draw(lists(binary()))]
5420
5421         class Seq(SetOf):
5422             schema = OctetString()
5423         seq = Seq(values)
5424         seq_encoded = seq.encode()
5425         seq_decoded, _ = seq.decode(seq_encoded)
5426         self.assertSequenceEqual(
5427             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5428             b"".join(sorted([v.encode() for v in values])),
5429         )
5430
5431
5432 class TestGoMarshalVectors(TestCase):
5433     def runTest(self):
5434         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
5435         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
5436         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
5437         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
5438         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
5439
5440         class Seq(Sequence):
5441             schema = (
5442                 ("erste", Integer()),
5443                 ("zweite", Integer(optional=True))
5444             )
5445         seq = Seq()
5446         seq["erste"] = Integer(64)
5447         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5448         seq["erste"] = Integer(0x123456)
5449         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
5450         seq["erste"] = Integer(64)
5451         seq["zweite"] = Integer(65)
5452         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
5453
5454         class NestedSeq(Sequence):
5455             schema = (
5456                 ("nest", Seq()),
5457             )
5458         seq["erste"] = Integer(127)
5459         seq["zweite"] = None
5460         nested = NestedSeq()
5461         nested["nest"] = seq
5462         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
5463
5464         self.assertSequenceEqual(
5465             OctetString(b"\x01\x02\x03").encode(),
5466             hexdec("0403010203"),
5467         )
5468
5469         class Seq(Sequence):
5470             schema = (
5471                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
5472             )
5473         seq = Seq()
5474         seq["erste"] = Integer(64)
5475         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
5476
5477         class Seq(Sequence):
5478             schema = (
5479                 ("erste", Integer(expl=tag_ctxc(5))),
5480             )
5481         seq = Seq()
5482         seq["erste"] = Integer(64)
5483         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
5484
5485         class Seq(Sequence):
5486             schema = (
5487                 ("erste", Null(
5488                     impl=tag_encode(0, klass=TagClassContext),
5489                     optional=True,
5490                 )),
5491             )
5492         seq = Seq()
5493         seq["erste"] = Null()
5494         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
5495         seq["erste"] = None
5496         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5497
5498         self.assertSequenceEqual(
5499             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
5500             hexdec("170d3730303130313030303030305a"),
5501         )
5502         self.assertSequenceEqual(
5503             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
5504             hexdec("170d3039313131353232353631365a"),
5505         )
5506         self.assertSequenceEqual(
5507             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
5508             hexdec("180f32313030303430353132303130315a"),
5509         )
5510
5511         class Seq(Sequence):
5512             schema = (
5513                 ("erste", GeneralizedTime()),
5514             )
5515         seq = Seq()
5516         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
5517         self.assertSequenceEqual(
5518             seq.encode(),
5519             hexdec("3011180f32303039313131353232353631365a"),
5520         )
5521
5522         self.assertSequenceEqual(
5523             BitString((1, b"\x80")).encode(),
5524             hexdec("03020780"),
5525         )
5526         self.assertSequenceEqual(
5527             BitString((12, b"\x81\xF0")).encode(),
5528             hexdec("03030481f0"),
5529         )
5530
5531         self.assertSequenceEqual(
5532             ObjectIdentifier("1.2.3.4").encode(),
5533             hexdec("06032a0304"),
5534         )
5535         self.assertSequenceEqual(
5536             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
5537             hexdec("06092a864888932d010105"),
5538         )
5539         self.assertSequenceEqual(
5540             ObjectIdentifier("2.100.3").encode(),
5541             hexdec("0603813403"),
5542         )
5543
5544         self.assertSequenceEqual(
5545             PrintableString("test").encode(),
5546             hexdec("130474657374"),
5547         )
5548         self.assertSequenceEqual(
5549             PrintableString("x" * 127).encode(),
5550             hexdec("137F" + "78" * 127),
5551         )
5552         self.assertSequenceEqual(
5553             PrintableString("x" * 128).encode(),
5554             hexdec("138180" + "78" * 128),
5555         )
5556         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
5557
5558         class Seq(Sequence):
5559             schema = (
5560                 ("erste", IA5String()),
5561             )
5562         seq = Seq()
5563         seq["erste"] = IA5String("test")
5564         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
5565
5566         class Seq(Sequence):
5567             schema = (
5568                 ("erste", PrintableString()),
5569             )
5570         seq = Seq()
5571         seq["erste"] = PrintableString("test")
5572         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
5573         seq["erste"] = PrintableString("test*")
5574         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
5575
5576         class Seq(Sequence):
5577             schema = (
5578                 ("erste", Any(optional=True)),
5579                 ("zweite", Integer()),
5580             )
5581         seq = Seq()
5582         seq["zweite"] = Integer(64)
5583         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5584
5585         class Seq(SetOf):
5586             schema = Integer()
5587         seq = Seq()
5588         seq.append(Integer(10))
5589         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
5590
5591         class _SeqOf(SequenceOf):
5592             schema = PrintableString()
5593
5594         class SeqOf(SequenceOf):
5595             schema = _SeqOf()
5596         _seqof = _SeqOf()
5597         _seqof.append(PrintableString("1"))
5598         seqof = SeqOf()
5599         seqof.append(_seqof)
5600         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
5601
5602         class Seq(Sequence):
5603             schema = (
5604                 ("erste", Integer(default=1)),
5605             )
5606         seq = Seq()
5607         seq["erste"] = Integer(0)
5608         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
5609         seq["erste"] = Integer(1)
5610         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5611         seq["erste"] = Integer(2)
5612         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
5613
5614
5615 class TestPP(TestCase):
5616     @given(data_strategy())
5617     def test_oid_printing(self, d):
5618         oids = {
5619             str(ObjectIdentifier(k)): v * 2
5620             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
5621         }
5622         chosen = d.draw(sampled_from(sorted(oids)))
5623         chosen_id = oids[chosen]
5624         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
5625         self.assertNotIn(chosen_id, pp_console_row(pp))
5626         self.assertIn(chosen_id, pp_console_row(pp, oids=oids))
5627
5628
5629 class TestAutoAddSlots(TestCase):
5630     def runTest(self):
5631         class Inher(Integer):
5632             pass
5633
5634         with self.assertRaises(AttributeError):
5635             inher = Inher()
5636             inher.unexistent = "whatever"
5637
5638
5639 class TestOIDDefines(TestCase):
5640     @given(data_strategy())
5641     def runTest(self, d):
5642         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
5643         value_name_chosen = d.draw(sampled_from(value_names))
5644         oids = [
5645             ObjectIdentifier(oid)
5646             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
5647         ]
5648         oid_chosen = d.draw(sampled_from(oids))
5649         values = d.draw(lists(
5650             integers(),
5651             min_size=len(value_names),
5652             max_size=len(value_names),
5653         ))
5654         _schema = [
5655             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
5656                 oid: Integer() for oid in oids[:-1]
5657             }),))),
5658         ]
5659         for i, value_name in enumerate(value_names):
5660             _schema.append((value_name, Any(expl=tag_ctxp(i))))
5661
5662         class Seq(Sequence):
5663             schema = _schema
5664         seq = Seq()
5665         for value_name, value in zip(value_names, values):
5666             seq[value_name] = Any(Integer(value).encode())
5667         seq["type"] = oid_chosen
5668         seq, _ = Seq().decode(seq.encode())
5669         for value_name in value_names:
5670             if value_name == value_name_chosen:
5671                 continue
5672             self.assertIsNone(seq[value_name].defined)
5673         if value_name_chosen in oids[:-1]:
5674             self.assertIsNotNone(seq[value_name_chosen].defined)
5675             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
5676             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
5677
5678
5679 class TestDefinesByPath(TestCase):
5680     def test_generated(self):
5681         class Seq(Sequence):
5682             schema = (
5683                 ("type", ObjectIdentifier()),
5684                 ("value", OctetString(expl=tag_ctxc(123))),
5685             )
5686
5687         class SeqInner(Sequence):
5688             schema = (
5689                 ("typeInner", ObjectIdentifier()),
5690                 ("valueInner", Any()),
5691             )
5692
5693         class PairValue(SetOf):
5694             schema = Any()
5695
5696         class Pair(Sequence):
5697             schema = (
5698                 ("type", ObjectIdentifier()),
5699                 ("value", PairValue()),
5700             )
5701
5702         class Pairs(SequenceOf):
5703             schema = Pair()
5704
5705         (
5706             type_integered,
5707             type_sequenced,
5708             type_innered,
5709             type_octet_stringed,
5710         ) = [
5711             ObjectIdentifier(oid)
5712             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
5713         ]
5714         seq_integered = Seq()
5715         seq_integered["type"] = type_integered
5716         seq_integered["value"] = OctetString(Integer(123).encode())
5717         seq_integered_raw = seq_integered.encode()
5718
5719         pairs = Pairs()
5720         pairs_input = (
5721             (type_octet_stringed, OctetString(b"whatever")),
5722             (type_integered, Integer(123)),
5723             (type_octet_stringed, OctetString(b"whenever")),
5724             (type_integered, Integer(234)),
5725         )
5726         for t, v in pairs_input:
5727             pair = Pair()
5728             pair["type"] = t
5729             pair["value"] = PairValue((Any(v),))
5730             pairs.append(pair)
5731         seq_inner = SeqInner()
5732         seq_inner["typeInner"] = type_innered
5733         seq_inner["valueInner"] = Any(pairs)
5734         seq_sequenced = Seq()
5735         seq_sequenced["type"] = type_sequenced
5736         seq_sequenced["value"] = OctetString(seq_inner.encode())
5737         seq_sequenced_raw = seq_sequenced.encode()
5738
5739         defines_by_path = []
5740         seq_integered, _ = Seq().decode(seq_integered_raw)
5741         self.assertIsNone(seq_integered["value"].defined)
5742         defines_by_path.append(
5743             (("type",), ((("value",), {
5744                 type_integered: Integer(),
5745                 type_sequenced: SeqInner(),
5746             }),))
5747         )
5748         seq_integered, _ = Seq().decode(
5749             seq_integered_raw,
5750             ctx={"defines_by_path": defines_by_path},
5751         )
5752         self.assertIsNotNone(seq_integered["value"].defined)
5753         self.assertEqual(seq_integered["value"].defined[0], type_integered)
5754         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
5755         self.assertTrue(seq_integered_raw[
5756             seq_integered["value"].defined[1].offset:
5757         ].startswith(Integer(123).encode()))
5758
5759         seq_sequenced, _ = Seq().decode(
5760             seq_sequenced_raw,
5761             ctx={"defines_by_path": defines_by_path},
5762         )
5763         self.assertIsNotNone(seq_sequenced["value"].defined)
5764         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5765         seq_inner = seq_sequenced["value"].defined[1]
5766         self.assertIsNone(seq_inner["valueInner"].defined)
5767
5768         defines_by_path.append((
5769             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
5770             ((("valueInner",), {type_innered: Pairs()}),),
5771         ))
5772         seq_sequenced, _ = Seq().decode(
5773             seq_sequenced_raw,
5774             ctx={"defines_by_path": defines_by_path},
5775         )
5776         self.assertIsNotNone(seq_sequenced["value"].defined)
5777         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5778         seq_inner = seq_sequenced["value"].defined[1]
5779         self.assertIsNotNone(seq_inner["valueInner"].defined)
5780         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5781         pairs = seq_inner["valueInner"].defined[1]
5782         for pair in pairs:
5783             self.assertIsNone(pair["value"][0].defined)
5784
5785         defines_by_path.append((
5786             (
5787                 "value",
5788                 DecodePathDefBy(type_sequenced),
5789                 "valueInner",
5790                 DecodePathDefBy(type_innered),
5791                 any,
5792                 "type",
5793             ),
5794             ((("value",), {
5795                 type_integered: Integer(),
5796                 type_octet_stringed: OctetString(),
5797             }),),
5798         ))
5799         seq_sequenced, _ = Seq().decode(
5800             seq_sequenced_raw,
5801             ctx={"defines_by_path": defines_by_path},
5802         )
5803         self.assertIsNotNone(seq_sequenced["value"].defined)
5804         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5805         seq_inner = seq_sequenced["value"].defined[1]
5806         self.assertIsNotNone(seq_inner["valueInner"].defined)
5807         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5808         pairs_got = seq_inner["valueInner"].defined[1]
5809         for pair_input, pair_got in zip(pairs_input, pairs_got):
5810             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
5811             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
5812
5813     @given(oid_strategy(), integers())
5814     def test_simple(self, oid, tgt):
5815         class Inner(Sequence):
5816             schema = (
5817                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
5818                     ObjectIdentifier(oid): Integer(),
5819                 }),))),
5820             )
5821
5822         class Outer(Sequence):
5823             schema = (
5824                 ("inner", Inner()),
5825                 ("tgt", OctetString()),
5826             )
5827
5828         inner = Inner()
5829         inner["oid"] = ObjectIdentifier(oid)
5830         outer = Outer()
5831         outer["inner"] = inner
5832         outer["tgt"] = OctetString(Integer(tgt).encode())
5833         decoded, _ = Outer().decode(outer.encode())
5834         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
5835
5836
5837 class TestAbsDecodePath(TestCase):
5838     @given(
5839         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5840         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5841     )
5842     def test_concat(self, decode_path, rel_path):
5843         self.assertSequenceEqual(
5844             abs_decode_path(decode_path, rel_path),
5845             decode_path + rel_path,
5846         )
5847
5848     @given(
5849         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5850         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5851     )
5852     def test_abs(self, decode_path, rel_path):
5853         self.assertSequenceEqual(
5854             abs_decode_path(decode_path, ("/",) + rel_path),
5855             rel_path,
5856         )
5857
5858     @given(
5859         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
5860         integers(min_value=1, max_value=3),
5861         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5862     )
5863     def test_dots(self, decode_path, number_of_dots, rel_path):
5864         self.assertSequenceEqual(
5865             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
5866             decode_path[:-number_of_dots] + rel_path,
5867         )
5868
5869
5870 class TestStrictDefaultExistence(TestCase):
5871     @given(data_strategy())
5872     def runTest(self, d):
5873         count = d.draw(integers(min_value=1, max_value=10))
5874         chosen = d.draw(integers(min_value=0, max_value=count - 1))
5875         _schema = [
5876             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
5877             for i in range(count)
5878         ]
5879         for klass in (Sequence, Set):
5880             class Seq(klass):
5881                 schema = _schema
5882             seq = Seq()
5883             for i in range(count):
5884                 seq["int%d" % i] = Integer(123)
5885             raw = seq.encode()
5886             chosen_choice = "int%d" % chosen
5887             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
5888             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5889                 seq.decode(raw)
5890             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
5891             self.assertTrue(decoded.bered)
5892             decoded, _ = seq.decode(raw, ctx={"bered": True})
5893             self.assertTrue(decoded.bered)
5894
5895
5896 class TestX690PrefixedType(TestCase):
5897     def runTest(self):
5898         self.assertSequenceEqual(
5899             VisibleString("Jones").encode(),
5900             hexdec("1A054A6F6E6573"),
5901         )
5902         self.assertSequenceEqual(
5903             VisibleString(
5904                 "Jones",
5905                 impl=tag_encode(3, klass=TagClassApplication),
5906             ).encode(),
5907             hexdec("43054A6F6E6573"),
5908         )
5909         self.assertSequenceEqual(
5910             Any(
5911                 VisibleString(
5912                     "Jones",
5913                     impl=tag_encode(3, klass=TagClassApplication),
5914                 ),
5915                 expl=tag_ctxc(2),
5916             ).encode(),
5917             hexdec("A20743054A6F6E6573"),
5918         )
5919         self.assertSequenceEqual(
5920             OctetString(
5921                 VisibleString(
5922                     "Jones",
5923                     impl=tag_encode(3, klass=TagClassApplication),
5924                 ).encode(),
5925                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
5926             ).encode(),
5927             hexdec("670743054A6F6E6573"),
5928         )
5929         self.assertSequenceEqual(
5930             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
5931             hexdec("82054A6F6E6573"),
5932         )
5933
5934
5935 class TestExplOOB(TestCase):
5936     def runTest(self):
5937         expl = tag_ctxc(123)
5938         raw = Integer(123).encode() + Integer(234).encode()
5939         raw = b"".join((expl, len_encode(len(raw)), raw))
5940         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
5941             Integer(expl=expl).decode(raw)
5942         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})