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