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