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