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