]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Forbid any later GNU GPL versions autousage
[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-2019 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
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         elif generation_choice == 2 or draw(booleans()):
1150             return draw(binary(max_size=len(schema) // 8))
1151         elif 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     @given(data_strategy())
2745     def test_nonnormalized_first_arc(self, d):
2746         tampered = (
2747             ObjectIdentifier.tag_default +
2748             len_encode(2) +
2749             b'\x80' +
2750             ObjectIdentifier((1, 0)).encode()[-1:]
2751         )
2752         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
2753         self.assertTrue(obj.ber_encoded)
2754         self.assertTrue(obj.bered)
2755         obj = obj.copy()
2756         self.assertTrue(obj.ber_encoded)
2757         self.assertTrue(obj.bered)
2758         with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
2759             ObjectIdentifier().decode(tampered)
2760
2761     @given(data_strategy())
2762     def test_nonnormalized_arcs(self, d):
2763         arcs = d.draw(lists(
2764             integers(min_value=0, max_value=100),
2765             min_size=1,
2766             max_size=5,
2767         ))
2768         dered = ObjectIdentifier((1, 0) + tuple(arcs)).encode()
2769         _, tlen, lv = tag_strip(dered)
2770         _, llen, v = len_decode(lv)
2771         v_no_first_arc = v[1:]
2772         idx_for_tamper = d.draw(integers(
2773             min_value=0,
2774             max_value=len(v_no_first_arc) - 1,
2775         ))
2776         tampered = list(bytearray(v_no_first_arc))
2777         for _ in range(d.draw(integers(min_value=1, max_value=3))):
2778             tampered.insert(idx_for_tamper, 0x80)
2779         tampered = bytes(bytearray(tampered))
2780         tampered = (
2781             ObjectIdentifier.tag_default +
2782             len_encode(len(tampered)) +
2783             tampered
2784         )
2785         obj, _ = ObjectIdentifier().decode(tampered, ctx={"bered": True})
2786         self.assertTrue(obj.ber_encoded)
2787         self.assertTrue(obj.bered)
2788         obj = obj.copy()
2789         self.assertTrue(obj.ber_encoded)
2790         self.assertTrue(obj.bered)
2791         with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"):
2792             ObjectIdentifier().decode(tampered)
2793
2794
2795 @composite
2796 def enumerated_values_strategy(draw, schema=None, do_expl=False):
2797     if schema is None:
2798         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
2799         values = list(draw(sets(
2800             integers(),
2801             min_size=len(schema),
2802             max_size=len(schema),
2803         )))
2804         schema = list(zip(schema, values))
2805     value = draw(one_of(none(), sampled_from([k for k, v in schema])))
2806     impl = None
2807     expl = None
2808     if do_expl:
2809         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2810     else:
2811         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2812     default = draw(one_of(none(), sampled_from([v for k, v in schema])))
2813     optional = draw(one_of(none(), booleans()))
2814     _decoded = (
2815         draw(integers(min_value=0)),
2816         draw(integers(min_value=0)),
2817         draw(integers(min_value=0)),
2818     )
2819     return (schema, value, impl, expl, default, optional, _decoded)
2820
2821
2822 class TestEnumerated(CommonMixin, TestCase):
2823     class EWhatever(Enumerated):
2824         schema = (("whatever", 0),)
2825
2826     base_klass = EWhatever
2827
2828     def test_schema_required(self):
2829         with assertRaisesRegex(self, ValueError, "schema must be specified"):
2830             Enumerated()
2831
2832     def test_invalid_value_type(self):
2833         with self.assertRaises(InvalidValueType) as err:
2834             self.base_klass((1, 2))
2835         repr(err.exception)
2836
2837     @given(sets(text_letters(), min_size=2))
2838     def test_unknown_name(self, schema_input):
2839         missing = schema_input.pop()
2840
2841         class E(Enumerated):
2842             schema = [(n, 123) for n in schema_input]
2843         with self.assertRaises(ObjUnknown) as err:
2844             E(missing)
2845         repr(err.exception)
2846
2847     @given(
2848         sets(text_letters(), min_size=2),
2849         sets(integers(), min_size=2),
2850     )
2851     def test_unknown_value(self, schema_input, values_input):
2852         schema_input.pop()
2853         missing_value = values_input.pop()
2854         _input = list(zip(schema_input, values_input))
2855
2856         class E(Enumerated):
2857             schema = _input
2858         with self.assertRaises(DecodeError) as err:
2859             E(missing_value)
2860         repr(err.exception)
2861
2862     @given(booleans())
2863     def test_optional(self, optional):
2864         obj = self.base_klass(default="whatever", optional=optional)
2865         self.assertTrue(obj.optional)
2866
2867     def test_ready(self):
2868         obj = self.base_klass()
2869         self.assertFalse(obj.ready)
2870         repr(obj)
2871         list(obj.pps())
2872         pprint(obj, big_blobs=True, with_decode_path=True)
2873         with self.assertRaises(ObjNotReady) as err:
2874             obj.encode()
2875         repr(err.exception)
2876         obj = self.base_klass("whatever")
2877         self.assertTrue(obj.ready)
2878         repr(obj)
2879         list(obj.pps())
2880         pprint(obj, big_blobs=True, with_decode_path=True)
2881
2882     @given(integers(), integers(), binary(), binary())
2883     def test_comparison(self, value1, value2, tag1, tag2):
2884         class E(Enumerated):
2885             schema = (
2886                 ("whatever0", value1),
2887                 ("whatever1", value2),
2888             )
2889
2890         class EInherited(E):
2891             pass
2892         for klass in (E, EInherited):
2893             obj1 = klass(value1)
2894             obj2 = klass(value2)
2895             self.assertEqual(obj1 == obj2, value1 == value2)
2896             self.assertEqual(obj1 != obj2, value1 != value2)
2897             self.assertEqual(obj1 == int(obj2), value1 == value2)
2898             obj1 = klass(value1, impl=tag1)
2899             obj2 = klass(value1, impl=tag2)
2900             self.assertEqual(obj1 == obj2, tag1 == tag2)
2901             self.assertEqual(obj1 != obj2, tag1 != tag2)
2902
2903     @given(data_strategy())
2904     def test_call(self, d):
2905         (
2906             schema_initial,
2907             value_initial,
2908             impl_initial,
2909             expl_initial,
2910             default_initial,
2911             optional_initial,
2912             _decoded_initial,
2913         ) = d.draw(enumerated_values_strategy())
2914
2915         class E(Enumerated):
2916             schema = schema_initial
2917         obj_initial = E(
2918             value=value_initial,
2919             impl=impl_initial,
2920             expl=expl_initial,
2921             default=default_initial,
2922             optional=optional_initial or False,
2923             _decoded=_decoded_initial,
2924         )
2925         (
2926             _,
2927             value,
2928             impl,
2929             expl,
2930             default,
2931             optional,
2932             _decoded,
2933         ) = d.draw(enumerated_values_strategy(
2934             schema=schema_initial,
2935             do_expl=impl_initial is None,
2936         ))
2937         obj = obj_initial(
2938             value=value,
2939             impl=impl,
2940             expl=expl,
2941             default=default,
2942             optional=optional,
2943         )
2944         if obj.ready:
2945             value_expected = default if value is None else value
2946             value_expected = (
2947                 default_initial if value_expected is None
2948                 else value_expected
2949             )
2950             self.assertEqual(
2951                 int(obj),
2952                 dict(schema_initial).get(value_expected, value_expected),
2953             )
2954         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2955         self.assertEqual(obj.expl_tag, expl or expl_initial)
2956         self.assertEqual(
2957             obj.default,
2958             default_initial if default is None else default,
2959         )
2960         if obj.default is None:
2961             optional = optional_initial if optional is None else optional
2962             optional = False if optional is None else optional
2963         else:
2964             optional = True
2965         self.assertEqual(obj.optional, optional)
2966         self.assertEqual(obj.specs, dict(schema_initial))
2967
2968     @given(enumerated_values_strategy())
2969     def test_copy(self, values):
2970         schema_input, value, impl, expl, default, optional, _decoded = values
2971
2972         class E(Enumerated):
2973             schema = schema_input
2974         obj = E(
2975             value=value,
2976             impl=impl,
2977             expl=expl,
2978             default=default,
2979             optional=optional,
2980             _decoded=_decoded,
2981         )
2982         obj_copied = obj.copy()
2983         self.assert_copied_basic_fields(obj, obj_copied)
2984         self.assertEqual(obj.specs, obj_copied.specs)
2985
2986     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2987     @given(data_strategy())
2988     def test_symmetric(self, d):
2989         schema_input, _, _, _, default, optional, _decoded = d.draw(
2990             enumerated_values_strategy(),
2991         )
2992         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
2993         offset = d.draw(integers(min_value=0))
2994         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
2995         tail_junk = d.draw(binary(max_size=5))
2996
2997         class E(Enumerated):
2998             schema = schema_input
2999         obj = E(
3000             value=value,
3001             default=default,
3002             optional=optional,
3003             _decoded=_decoded,
3004         )
3005         repr(obj)
3006         list(obj.pps())
3007         pprint(obj, big_blobs=True, with_decode_path=True)
3008         self.assertFalse(obj.expled)
3009         obj_encoded = obj.encode()
3010         obj_expled = obj(value, expl=tag_expl)
3011         self.assertTrue(obj_expled.expled)
3012         repr(obj_expled)
3013         list(obj_expled.pps())
3014         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3015         obj_expled_encoded = obj_expled.encode()
3016         ctx_copied = deepcopy(ctx_dummy)
3017         obj_decoded, tail = obj_expled.decode(
3018             obj_expled_encoded + tail_junk,
3019             offset=offset,
3020             ctx=ctx_copied,
3021         )
3022         self.assertDictEqual(ctx_copied, ctx_dummy)
3023         repr(obj_decoded)
3024         list(obj_decoded.pps())
3025         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3026         self.assertEqual(tail, tail_junk)
3027         self.assertEqual(obj_decoded, obj_expled)
3028         self.assertNotEqual(obj_decoded, obj)
3029         self.assertEqual(int(obj_decoded), int(obj_expled))
3030         self.assertEqual(int(obj_decoded), int(obj))
3031         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3032         self.assertEqual(obj_decoded.expl_tag, tag_expl)
3033         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3034         self.assertEqual(
3035             obj_decoded.expl_llen,
3036             len(len_encode(len(obj_encoded))),
3037         )
3038         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3039         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3040         self.assertEqual(
3041             obj_decoded.offset,
3042             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3043         )
3044         self.assertEqual(obj_decoded.expl_offset, offset)
3045
3046
3047 @composite
3048 def string_values_strategy(draw, alphabet, do_expl=False):
3049     bound_min, bound_max = sorted(draw(sets(
3050         integers(min_value=0, max_value=1 << 7),
3051         min_size=2,
3052         max_size=2,
3053     )))
3054     value = draw(one_of(
3055         none(),
3056         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3057     ))
3058     default = draw(one_of(
3059         none(),
3060         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
3061     ))
3062     bounds = None
3063     if draw(booleans()):
3064         bounds = (bound_min, bound_max)
3065     impl = None
3066     expl = None
3067     if do_expl:
3068         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3069     else:
3070         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3071     optional = draw(one_of(none(), booleans()))
3072     _decoded = (
3073         draw(integers(min_value=0)),
3074         draw(integers(min_value=0)),
3075         draw(integers(min_value=0)),
3076     )
3077     return (value, bounds, impl, expl, default, optional, _decoded)
3078
3079
3080 class StringMixin(object):
3081     def test_invalid_value_type(self):
3082         with self.assertRaises(InvalidValueType) as err:
3083             self.base_klass((1, 2))
3084         repr(err.exception)
3085
3086     def text_alphabet(self):
3087         if self.base_klass.encoding in ("ascii", "iso-8859-1"):
3088             return printable + whitespace
3089         return None
3090
3091     @given(booleans())
3092     def test_optional(self, optional):
3093         obj = self.base_klass(default=self.base_klass(""), optional=optional)
3094         self.assertTrue(obj.optional)
3095
3096     @given(data_strategy())
3097     def test_ready(self, d):
3098         obj = self.base_klass()
3099         self.assertFalse(obj.ready)
3100         repr(obj)
3101         list(obj.pps())
3102         pprint(obj, big_blobs=True, with_decode_path=True)
3103         text_type(obj)
3104         with self.assertRaises(ObjNotReady) as err:
3105             obj.encode()
3106         repr(err.exception)
3107         value = d.draw(text(alphabet=self.text_alphabet()))
3108         obj = self.base_klass(value)
3109         self.assertTrue(obj.ready)
3110         repr(obj)
3111         list(obj.pps())
3112         pprint(obj, big_blobs=True, with_decode_path=True)
3113         text_type(obj)
3114
3115     @given(data_strategy())
3116     def test_comparison(self, d):
3117         value1 = d.draw(text(alphabet=self.text_alphabet()))
3118         value2 = d.draw(text(alphabet=self.text_alphabet()))
3119         tag1 = d.draw(binary(min_size=1))
3120         tag2 = d.draw(binary(min_size=1))
3121         obj1 = self.base_klass(value1)
3122         obj2 = self.base_klass(value2)
3123         self.assertEqual(obj1 == obj2, value1 == value2)
3124         self.assertEqual(obj1 != obj2, value1 != value2)
3125         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3126         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
3127         obj1 = self.base_klass(value1, impl=tag1)
3128         obj2 = self.base_klass(value1, impl=tag2)
3129         self.assertEqual(obj1 == obj2, tag1 == tag2)
3130         self.assertEqual(obj1 != obj2, tag1 != tag2)
3131
3132     @given(data_strategy())
3133     def test_bounds_satisfied(self, d):
3134         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
3135         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3136         value = d.draw(text(
3137             alphabet=self.text_alphabet(),
3138             min_size=bound_min,
3139             max_size=bound_max,
3140         ))
3141         self.base_klass(value=value, bounds=(bound_min, bound_max))
3142
3143     @given(data_strategy())
3144     def test_bounds_unsatisfied(self, d):
3145         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
3146         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3147         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
3148         with self.assertRaises(BoundsError) as err:
3149             self.base_klass(value=value, bounds=(bound_min, bound_max))
3150         repr(err.exception)
3151         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3152             self.base_klass(bounds=(bound_min, bound_max)).decode(
3153                 self.base_klass(value).encode()
3154             )
3155         repr(err.exception)
3156         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
3157         with self.assertRaises(BoundsError) as err:
3158             self.base_klass(value=value, bounds=(bound_min, bound_max))
3159         repr(err.exception)
3160         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3161             self.base_klass(bounds=(bound_min, bound_max)).decode(
3162                 self.base_klass(value).encode()
3163             )
3164         repr(err.exception)
3165
3166     @given(data_strategy())
3167     def test_call(self, d):
3168         (
3169             value_initial,
3170             bounds_initial,
3171             impl_initial,
3172             expl_initial,
3173             default_initial,
3174             optional_initial,
3175             _decoded_initial,
3176         ) = d.draw(string_values_strategy(self.text_alphabet()))
3177         obj_initial = self.base_klass(
3178             value_initial,
3179             bounds_initial,
3180             impl_initial,
3181             expl_initial,
3182             default_initial,
3183             optional_initial or False,
3184             _decoded_initial,
3185         )
3186         (
3187             value,
3188             bounds,
3189             impl,
3190             expl,
3191             default,
3192             optional,
3193             _decoded,
3194         ) = d.draw(string_values_strategy(
3195             self.text_alphabet(),
3196             do_expl=impl_initial is None,
3197         ))
3198         if (default is None) and (obj_initial.default is not None):
3199             bounds = None
3200         if (
3201                 (bounds is None) and
3202                 (value is not None) and
3203                 (bounds_initial is not None) and
3204                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
3205         ):
3206             value = None
3207         if (
3208                 (bounds is None) and
3209                 (default is not None) and
3210                 (bounds_initial is not None) and
3211                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
3212         ):
3213             default = None
3214         obj = obj_initial(value, bounds, impl, expl, default, optional)
3215         if obj.ready:
3216             value_expected = default if value is None else value
3217             value_expected = (
3218                 default_initial if value_expected is None
3219                 else value_expected
3220             )
3221             self.assertEqual(obj, value_expected)
3222         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3223         self.assertEqual(obj.expl_tag, expl or expl_initial)
3224         self.assertEqual(
3225             obj.default,
3226             default_initial if default is None else default,
3227         )
3228         if obj.default is None:
3229             optional = optional_initial if optional is None else optional
3230             optional = False if optional is None else optional
3231         else:
3232             optional = True
3233         self.assertEqual(obj.optional, optional)
3234         self.assertEqual(
3235             (obj._bound_min, obj._bound_max),
3236             bounds or bounds_initial or (0, float("+inf")),
3237         )
3238
3239     @given(data_strategy())
3240     def test_copy(self, d):
3241         values = d.draw(string_values_strategy(self.text_alphabet()))
3242         obj = self.base_klass(*values)
3243         obj_copied = obj.copy()
3244         self.assert_copied_basic_fields(obj, obj_copied)
3245         self.assertEqual(obj._bound_min, obj_copied._bound_min)
3246         self.assertEqual(obj._bound_max, obj_copied._bound_max)
3247         self.assertEqual(obj._value, obj_copied._value)
3248
3249     @given(data_strategy())
3250     def test_stripped(self, d):
3251         value = d.draw(text(alphabet=self.text_alphabet()))
3252         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3253         obj = self.base_klass(value, impl=tag_impl)
3254         with self.assertRaises(NotEnoughData):
3255             obj.decode(obj.encode()[:-1])
3256
3257     @given(data_strategy())
3258     def test_stripped_expl(self, d):
3259         value = d.draw(text(alphabet=self.text_alphabet()))
3260         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3261         obj = self.base_klass(value, expl=tag_expl)
3262         with self.assertRaises(NotEnoughData):
3263             obj.decode(obj.encode()[:-1])
3264
3265     @given(
3266         integers(min_value=31),
3267         integers(min_value=0),
3268         decode_path_strat,
3269     )
3270     def test_bad_tag(self, tag, offset, decode_path):
3271         with self.assertRaises(DecodeError) as err:
3272             self.base_klass().decode(
3273                 tag_encode(tag)[:-1],
3274                 offset=offset,
3275                 decode_path=decode_path,
3276             )
3277         repr(err.exception)
3278         self.assertEqual(err.exception.offset, offset)
3279         self.assertEqual(err.exception.decode_path, decode_path)
3280
3281     @given(
3282         integers(min_value=128),
3283         integers(min_value=0),
3284         decode_path_strat,
3285     )
3286     def test_bad_len(self, l, offset, decode_path):
3287         with self.assertRaises(DecodeError) as err:
3288             self.base_klass().decode(
3289                 self.base_klass.tag_default + len_encode(l)[:-1],
3290                 offset=offset,
3291                 decode_path=decode_path,
3292             )
3293         repr(err.exception)
3294         self.assertEqual(err.exception.offset, offset)
3295         self.assertEqual(err.exception.decode_path, decode_path)
3296
3297     @given(
3298         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3299         integers(min_value=0),
3300         decode_path_strat,
3301     )
3302     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3303         value, bound_min = list(sorted(ints))
3304
3305         class String(self.base_klass):
3306             # Multiply this value by four, to satisfy UTF-32 bounds
3307             # (4 bytes per character) validation
3308             bounds = (bound_min * 4, bound_min * 4)
3309         with self.assertRaises(DecodeError) as err:
3310             String().decode(
3311                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
3312                 offset=offset,
3313                 decode_path=decode_path,
3314             )
3315         repr(err.exception)
3316         self.assertEqual(err.exception.offset, offset)
3317         self.assertEqual(err.exception.decode_path, decode_path)
3318
3319     @given(data_strategy())
3320     def test_symmetric(self, d):
3321         values = d.draw(string_values_strategy(self.text_alphabet()))
3322         value = d.draw(text(alphabet=self.text_alphabet()))
3323         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3324         offset = d.draw(integers(min_value=0))
3325         tail_junk = d.draw(binary(max_size=5))
3326         _, _, _, _, default, optional, _decoded = values
3327         obj = self.base_klass(
3328             value=value,
3329             default=default,
3330             optional=optional,
3331             _decoded=_decoded,
3332         )
3333         repr(obj)
3334         list(obj.pps())
3335         pprint(obj, big_blobs=True, with_decode_path=True)
3336         self.assertFalse(obj.expled)
3337         obj_encoded = obj.encode()
3338         obj_expled = obj(value, expl=tag_expl)
3339         self.assertTrue(obj_expled.expled)
3340         repr(obj_expled)
3341         list(obj_expled.pps())
3342         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3343         obj_expled_encoded = obj_expled.encode()
3344         ctx_copied = deepcopy(ctx_dummy)
3345         obj_decoded, tail = obj_expled.decode(
3346             obj_expled_encoded + tail_junk,
3347             offset=offset,
3348             ctx=ctx_copied,
3349         )
3350         self.assertDictEqual(ctx_copied, ctx_dummy)
3351         repr(obj_decoded)
3352         list(obj_decoded.pps())
3353         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3354         self.assertEqual(tail, tail_junk)
3355         self.assertEqual(obj_decoded, obj_expled)
3356         self.assertNotEqual(obj_decoded, obj)
3357         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3358         self.assertEqual(bytes(obj_decoded), bytes(obj))
3359         self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
3360         self.assertEqual(text_type(obj_decoded), text_type(obj))
3361         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3362         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3363         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3364         self.assertEqual(
3365             obj_decoded.expl_llen,
3366             len(len_encode(len(obj_encoded))),
3367         )
3368         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3369         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3370         self.assertEqual(
3371             obj_decoded.offset,
3372             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3373         )
3374         self.assertEqual(obj_decoded.expl_offset, offset)
3375
3376
3377 class TestUTF8String(StringMixin, CommonMixin, TestCase):
3378     base_klass = UTF8String
3379
3380
3381 cyrillic_letters = text(
3382     alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
3383     min_size=1,
3384     max_size=5,
3385 )
3386
3387
3388 class UnicodeDecodeErrorMixin(object):
3389     @given(cyrillic_letters)
3390     def test_unicode_decode_error(self, cyrillic_text):
3391         with self.assertRaises(DecodeError):
3392             self.base_klass(cyrillic_text)
3393
3394
3395 class TestNumericString(StringMixin, CommonMixin, TestCase):
3396     base_klass = NumericString
3397
3398     def text_alphabet(self):
3399         return digits + " "
3400
3401     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
3402     def test_non_numeric(self, non_numeric_text):
3403         with assertRaisesRegex(self, DecodeError, "non-numeric"):
3404             self.base_klass(non_numeric_text)
3405
3406     @given(
3407         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3408         integers(min_value=0),
3409         decode_path_strat,
3410     )
3411     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3412         value, bound_min = list(sorted(ints))
3413
3414         class String(self.base_klass):
3415             bounds = (bound_min, bound_min)
3416         with self.assertRaises(DecodeError) as err:
3417             String().decode(
3418                 self.base_klass(b"1" * value).encode(),
3419                 offset=offset,
3420                 decode_path=decode_path,
3421             )
3422         repr(err.exception)
3423         self.assertEqual(err.exception.offset, offset)
3424         self.assertEqual(err.exception.decode_path, decode_path)
3425
3426
3427 class TestPrintableString(
3428         UnicodeDecodeErrorMixin,
3429         StringMixin,
3430         CommonMixin,
3431         TestCase,
3432 ):
3433     base_klass = PrintableString
3434
3435     def text_alphabet(self):
3436         return ascii_letters + digits + " '()+,-./:=?"
3437
3438     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
3439     def test_non_printable(self, non_printable_text):
3440         with assertRaisesRegex(self, DecodeError, "non-printable"):
3441             self.base_klass(non_printable_text)
3442
3443     @given(
3444         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3445         integers(min_value=0),
3446         decode_path_strat,
3447     )
3448     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3449         value, bound_min = list(sorted(ints))
3450
3451         class String(self.base_klass):
3452             bounds = (bound_min, bound_min)
3453         with self.assertRaises(DecodeError) as err:
3454             String().decode(
3455                 self.base_klass(b"1" * value).encode(),
3456                 offset=offset,
3457                 decode_path=decode_path,
3458             )
3459         repr(err.exception)
3460         self.assertEqual(err.exception.offset, offset)
3461         self.assertEqual(err.exception.decode_path, decode_path)
3462
3463
3464 class TestTeletexString(
3465         UnicodeDecodeErrorMixin,
3466         StringMixin,
3467         CommonMixin,
3468         TestCase,
3469 ):
3470     base_klass = TeletexString
3471
3472
3473 class TestVideotexString(
3474         UnicodeDecodeErrorMixin,
3475         StringMixin,
3476         CommonMixin,
3477         TestCase,
3478 ):
3479     base_klass = VideotexString
3480
3481
3482 class TestIA5String(
3483         UnicodeDecodeErrorMixin,
3484         StringMixin,
3485         CommonMixin,
3486         TestCase,
3487 ):
3488     base_klass = IA5String
3489
3490
3491 class TestGraphicString(
3492         UnicodeDecodeErrorMixin,
3493         StringMixin,
3494         CommonMixin,
3495         TestCase,
3496 ):
3497     base_klass = GraphicString
3498
3499
3500 class TestVisibleString(
3501         UnicodeDecodeErrorMixin,
3502         StringMixin,
3503         CommonMixin,
3504         TestCase,
3505 ):
3506     base_klass = VisibleString
3507
3508     def test_x690_vector(self):
3509         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3510         self.assertSequenceEqual(tail, b"")
3511         self.assertEqual(str(obj), "Jones")
3512         self.assertFalse(obj.ber_encoded)
3513         self.assertFalse(obj.lenindef)
3514         self.assertFalse(obj.bered)
3515
3516         obj, tail = VisibleString().decode(
3517             hexdec("3A0904034A6F6E04026573"),
3518             ctx={"bered": True},
3519         )
3520         self.assertSequenceEqual(tail, b"")
3521         self.assertEqual(str(obj), "Jones")
3522         self.assertTrue(obj.ber_encoded)
3523         self.assertFalse(obj.lenindef)
3524         self.assertTrue(obj.bered)
3525         obj = obj.copy()
3526         self.assertTrue(obj.ber_encoded)
3527         self.assertFalse(obj.lenindef)
3528         self.assertTrue(obj.bered)
3529
3530         obj, tail = VisibleString().decode(
3531             hexdec("3A8004034A6F6E040265730000"),
3532             ctx={"bered": True},
3533         )
3534         self.assertSequenceEqual(tail, b"")
3535         self.assertEqual(str(obj), "Jones")
3536         self.assertTrue(obj.ber_encoded)
3537         self.assertTrue(obj.lenindef)
3538         self.assertTrue(obj.bered)
3539         obj = obj.copy()
3540         self.assertTrue(obj.ber_encoded)
3541         self.assertTrue(obj.lenindef)
3542         self.assertTrue(obj.bered)
3543
3544
3545 class TestGeneralString(
3546         UnicodeDecodeErrorMixin,
3547         StringMixin,
3548         CommonMixin,
3549         TestCase,
3550 ):
3551     base_klass = GeneralString
3552
3553
3554 class TestUniversalString(StringMixin, CommonMixin, TestCase):
3555     base_klass = UniversalString
3556
3557
3558 class TestBMPString(StringMixin, CommonMixin, TestCase):
3559     base_klass = BMPString
3560
3561
3562 @composite
3563 def generalized_time_values_strategy(
3564         draw,
3565         min_datetime,
3566         max_datetime,
3567         omit_ms=False,
3568         do_expl=False,
3569 ):
3570     value = None
3571     if draw(booleans()):
3572         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3573         if omit_ms:
3574             value = value.replace(microsecond=0)
3575     default = None
3576     if draw(booleans()):
3577         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3578         if omit_ms:
3579             default = default.replace(microsecond=0)
3580     impl = None
3581     expl = None
3582     if do_expl:
3583         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3584     else:
3585         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3586     optional = draw(one_of(none(), booleans()))
3587     _decoded = (
3588         draw(integers(min_value=0)),
3589         draw(integers(min_value=0)),
3590         draw(integers(min_value=0)),
3591     )
3592     return (value, impl, expl, default, optional, _decoded)
3593
3594
3595 class TimeMixin(object):
3596     def test_invalid_value_type(self):
3597         with self.assertRaises(InvalidValueType) as err:
3598             self.base_klass(datetime.now().timetuple())
3599         repr(err.exception)
3600
3601     @given(data_strategy())
3602     def test_optional(self, d):
3603         default = d.draw(datetimes(
3604             min_value=self.min_datetime,
3605             max_value=self.max_datetime,
3606         ))
3607         optional = d.draw(booleans())
3608         obj = self.base_klass(default=default, optional=optional)
3609         self.assertTrue(obj.optional)
3610
3611     @given(data_strategy())
3612     def test_ready(self, d):
3613         obj = self.base_klass()
3614         self.assertFalse(obj.ready)
3615         repr(obj)
3616         list(obj.pps())
3617         pprint(obj, big_blobs=True, with_decode_path=True)
3618         with self.assertRaises(ObjNotReady) as err:
3619             obj.encode()
3620         repr(err.exception)
3621         value = d.draw(datetimes(min_value=self.min_datetime))
3622         obj = self.base_klass(value)
3623         self.assertTrue(obj.ready)
3624         repr(obj)
3625         list(obj.pps())
3626         pprint(obj, big_blobs=True, with_decode_path=True)
3627
3628     @given(data_strategy())
3629     def test_comparison(self, d):
3630         value1 = d.draw(datetimes(
3631             min_value=self.min_datetime,
3632             max_value=self.max_datetime,
3633         ))
3634         value2 = d.draw(datetimes(
3635             min_value=self.min_datetime,
3636             max_value=self.max_datetime,
3637         ))
3638         tag1 = d.draw(binary(min_size=1))
3639         tag2 = d.draw(binary(min_size=1))
3640         if self.omit_ms:
3641             value1 = value1.replace(microsecond=0)
3642             value2 = value2.replace(microsecond=0)
3643         obj1 = self.base_klass(value1)
3644         obj2 = self.base_klass(value2)
3645         self.assertEqual(obj1 == obj2, value1 == value2)
3646         self.assertEqual(obj1 != obj2, value1 != value2)
3647         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
3648         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3649         obj1 = self.base_klass(value1, impl=tag1)
3650         obj2 = self.base_klass(value1, impl=tag2)
3651         self.assertEqual(obj1 == obj2, tag1 == tag2)
3652         self.assertEqual(obj1 != obj2, tag1 != tag2)
3653
3654     @given(data_strategy())
3655     def test_call(self, d):
3656         (
3657             value_initial,
3658             impl_initial,
3659             expl_initial,
3660             default_initial,
3661             optional_initial,
3662             _decoded_initial,
3663         ) = d.draw(generalized_time_values_strategy(
3664             min_datetime=self.min_datetime,
3665             max_datetime=self.max_datetime,
3666             omit_ms=self.omit_ms,
3667         ))
3668         obj_initial = self.base_klass(
3669             value=value_initial,
3670             impl=impl_initial,
3671             expl=expl_initial,
3672             default=default_initial,
3673             optional=optional_initial or False,
3674             _decoded=_decoded_initial,
3675         )
3676         (
3677             value,
3678             impl,
3679             expl,
3680             default,
3681             optional,
3682             _decoded,
3683         ) = d.draw(generalized_time_values_strategy(
3684             min_datetime=self.min_datetime,
3685             max_datetime=self.max_datetime,
3686             omit_ms=self.omit_ms,
3687             do_expl=impl_initial is None,
3688         ))
3689         obj = obj_initial(
3690             value=value,
3691             impl=impl,
3692             expl=expl,
3693             default=default,
3694             optional=optional,
3695         )
3696         if obj.ready:
3697             value_expected = default if value is None else value
3698             value_expected = (
3699                 default_initial if value_expected is None
3700                 else value_expected
3701             )
3702             self.assertEqual(obj, value_expected)
3703         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3704         self.assertEqual(obj.expl_tag, expl or expl_initial)
3705         self.assertEqual(
3706             obj.default,
3707             default_initial if default is None else default,
3708         )
3709         if obj.default is None:
3710             optional = optional_initial if optional is None else optional
3711             optional = False if optional is None else optional
3712         else:
3713             optional = True
3714         self.assertEqual(obj.optional, optional)
3715
3716     @given(data_strategy())
3717     def test_copy(self, d):
3718         values = d.draw(generalized_time_values_strategy(
3719             min_datetime=self.min_datetime,
3720             max_datetime=self.max_datetime,
3721         ))
3722         obj = self.base_klass(*values)
3723         obj_copied = obj.copy()
3724         self.assert_copied_basic_fields(obj, obj_copied)
3725         self.assertEqual(obj._value, obj_copied._value)
3726
3727     @given(data_strategy())
3728     def test_stripped(self, d):
3729         value = d.draw(datetimes(
3730             min_value=self.min_datetime,
3731             max_value=self.max_datetime,
3732         ))
3733         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3734         obj = self.base_klass(value, impl=tag_impl)
3735         with self.assertRaises(NotEnoughData):
3736             obj.decode(obj.encode()[:-1])
3737
3738     @given(data_strategy())
3739     def test_stripped_expl(self, d):
3740         value = d.draw(datetimes(
3741             min_value=self.min_datetime,
3742             max_value=self.max_datetime,
3743         ))
3744         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3745         obj = self.base_klass(value, expl=tag_expl)
3746         with self.assertRaises(NotEnoughData):
3747             obj.decode(obj.encode()[:-1])
3748
3749     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3750     @given(data_strategy())
3751     def test_symmetric(self, d):
3752         values = d.draw(generalized_time_values_strategy(
3753             min_datetime=self.min_datetime,
3754             max_datetime=self.max_datetime,
3755         ))
3756         value = d.draw(datetimes(
3757             min_value=self.min_datetime,
3758             max_value=self.max_datetime,
3759         ))
3760         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3761         offset = d.draw(integers(min_value=0))
3762         tail_junk = d.draw(binary(max_size=5))
3763         _, _, _, default, optional, _decoded = values
3764         obj = self.base_klass(
3765             value=value,
3766             default=default,
3767             optional=optional,
3768             _decoded=_decoded,
3769         )
3770         repr(obj)
3771         list(obj.pps())
3772         pprint(obj, big_blobs=True, with_decode_path=True)
3773         self.assertFalse(obj.expled)
3774         obj_encoded = obj.encode()
3775         self.additional_symmetric_check(value, obj_encoded)
3776         obj_expled = obj(value, expl=tag_expl)
3777         self.assertTrue(obj_expled.expled)
3778         repr(obj_expled)
3779         list(obj_expled.pps())
3780         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3781         obj_expled_encoded = obj_expled.encode()
3782         ctx_copied = deepcopy(ctx_dummy)
3783         obj_decoded, tail = obj_expled.decode(
3784             obj_expled_encoded + tail_junk,
3785             offset=offset,
3786             ctx=ctx_copied,
3787         )
3788         self.assertDictEqual(ctx_copied, ctx_dummy)
3789         repr(obj_decoded)
3790         list(obj_decoded.pps())
3791         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3792         self.assertEqual(tail, tail_junk)
3793         self.assertEqual(obj_decoded, obj_expled)
3794         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
3795         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
3796         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3797         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3798         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3799         self.assertEqual(
3800             obj_decoded.expl_llen,
3801             len(len_encode(len(obj_encoded))),
3802         )
3803         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3804         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3805         self.assertEqual(
3806             obj_decoded.offset,
3807             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3808         )
3809         self.assertEqual(obj_decoded.expl_offset, offset)
3810
3811
3812 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
3813     base_klass = GeneralizedTime
3814     omit_ms = False
3815     min_datetime = datetime(1900, 1, 1)
3816     max_datetime = datetime(9999, 12, 31)
3817
3818     def additional_symmetric_check(self, value, obj_encoded):
3819         if value.microsecond > 0:
3820             self.assertFalse(obj_encoded.endswith(b"0Z"))
3821
3822     def test_x690_vector_valid(self):
3823         for data in ((
3824                 b"19920521000000Z",
3825                 b"19920622123421Z",
3826                 b"19920722132100.3Z",
3827         )):
3828             GeneralizedTime(data)
3829
3830     def test_x690_vector_invalid(self):
3831         for data in ((
3832                 b"19920520240000Z",
3833                 b"19920622123421.0Z",
3834                 b"19920722132100.30Z",
3835         )):
3836             with self.assertRaises(DecodeError) as err:
3837                 GeneralizedTime(data)
3838             repr(err.exception)
3839
3840     def test_go_vectors_invalid(self):
3841         for data in ((
3842                 b"20100102030405",
3843                 b"00000100000000Z",
3844                 b"20101302030405Z",
3845                 b"20100002030405Z",
3846                 b"20100100030405Z",
3847                 b"20100132030405Z",
3848                 b"20100231030405Z",
3849                 b"20100102240405Z",
3850                 b"20100102036005Z",
3851                 b"20100102030460Z",
3852                 b"-20100102030410Z",
3853                 b"2010-0102030410Z",
3854                 b"2010-0002030410Z",
3855                 b"201001-02030410Z",
3856                 b"20100102-030410Z",
3857                 b"2010010203-0410Z",
3858                 b"201001020304-10Z",
3859                 # These ones are INVALID in *DER*, but accepted
3860                 # by Go's encoding/asn1
3861                 b"20100102030405+0607",
3862                 b"20100102030405-0607",
3863         )):
3864             with self.assertRaises(DecodeError) as err:
3865                 GeneralizedTime(data)
3866             repr(err.exception)
3867
3868     def test_go_vectors_valid(self):
3869         self.assertEqual(
3870             GeneralizedTime(b"20100102030405Z").todatetime(),
3871             datetime(2010, 1, 2, 3, 4, 5, 0),
3872         )
3873
3874     @given(
3875         binary(
3876             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3877             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3878         ),
3879         binary(min_size=1, max_size=1),
3880         binary(
3881             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3882             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3883         ),
3884     )
3885     def test_junk(self, part0, part1, part2):
3886         junk = part0 + part1 + part2
3887         assume(not (set(junk) <= set(digits.encode("ascii"))))
3888         with self.assertRaises(DecodeError):
3889             GeneralizedTime().decode(
3890                 GeneralizedTime.tag_default +
3891                 len_encode(len(junk)) +
3892                 junk
3893             )
3894
3895     @given(
3896         binary(
3897             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3898             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3899         ),
3900         binary(min_size=1, max_size=1),
3901         binary(
3902             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3903             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
3904         ),
3905     )
3906     def test_junk_dm(self, part0, part1, part2):
3907         junk = part0 + part1 + part2
3908         assume(not (set(junk) <= set(digits.encode("ascii"))))
3909         with self.assertRaises(DecodeError):
3910             GeneralizedTime().decode(
3911                 GeneralizedTime.tag_default +
3912                 len_encode(len(junk)) +
3913                 junk
3914             )
3915
3916     def test_ns_fractions(self):
3917         GeneralizedTime(b"20010101000000.000001Z")
3918         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
3919             GeneralizedTime(b"20010101000000.0000001Z")
3920
3921
3922 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
3923     base_klass = UTCTime
3924     omit_ms = True
3925     min_datetime = datetime(2000, 1, 1)
3926     max_datetime = datetime(2049, 12, 31)
3927
3928     def additional_symmetric_check(self, value, obj_encoded):
3929         pass
3930
3931     def test_x690_vector_valid(self):
3932         for data in ((
3933                 b"920521000000Z",
3934                 b"920622123421Z",
3935                 b"920722132100Z",
3936         )):
3937             UTCTime(data)
3938
3939     def test_x690_vector_invalid(self):
3940         for data in ((
3941                 b"920520240000Z",
3942                 b"9207221321Z",
3943         )):
3944             with self.assertRaises(DecodeError) as err:
3945                 UTCTime(data)
3946             repr(err.exception)
3947
3948     def test_go_vectors_invalid(self):
3949         for data in ((
3950                 b"a10506234540Z",
3951                 b"91a506234540Z",
3952                 b"9105a6234540Z",
3953                 b"910506a34540Z",
3954                 b"910506334a40Z",
3955                 b"91050633444aZ",
3956                 b"910506334461Z",
3957                 b"910506334400Za",
3958                 b"000100000000Z",
3959                 b"101302030405Z",
3960                 b"100002030405Z",
3961                 b"100100030405Z",
3962                 b"100132030405Z",
3963                 b"100231030405Z",
3964                 b"100102240405Z",
3965                 b"100102036005Z",
3966                 b"100102030460Z",
3967                 b"-100102030410Z",
3968                 b"10-0102030410Z",
3969                 b"10-0002030410Z",
3970                 b"1001-02030410Z",
3971                 b"100102-030410Z",
3972                 b"10010203-0410Z",
3973                 b"1001020304-10Z",
3974                 # These ones are INVALID in *DER*, but accepted
3975                 # by Go's encoding/asn1
3976                 b"910506164540-0700",
3977                 b"910506164540+0730",
3978                 b"9105062345Z",
3979                 b"5105062345Z",
3980         )):
3981             with self.assertRaises(DecodeError) as err:
3982                 UTCTime(data)
3983             repr(err.exception)
3984
3985     def test_go_vectors_valid(self):
3986         self.assertEqual(
3987             UTCTime(b"910506234540Z").todatetime(),
3988             datetime(1991, 5, 6, 23, 45, 40, 0),
3989         )
3990
3991     @given(integers(min_value=0, max_value=49))
3992     def test_pre50(self, year):
3993         self.assertEqual(
3994             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3995             2000 + year,
3996         )
3997
3998     @given(integers(min_value=50, max_value=99))
3999     def test_post50(self, year):
4000         self.assertEqual(
4001             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4002             1900 + year,
4003         )
4004
4005     @given(
4006         binary(
4007             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4008             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4009         ),
4010         binary(min_size=1, max_size=1),
4011         binary(
4012             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4013             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4014         ),
4015     )
4016     def test_junk(self, part0, part1, part2):
4017         junk = part0 + part1 + part2
4018         assume(not (set(junk) <= set(digits.encode("ascii"))))
4019         with self.assertRaises(DecodeError):
4020             UTCTime().decode(
4021                 UTCTime.tag_default +
4022                 len_encode(len(junk)) +
4023                 junk
4024             )
4025
4026
4027 @composite
4028 def any_values_strategy(draw, do_expl=False):
4029     value = draw(one_of(none(), binary()))
4030     expl = None
4031     if do_expl:
4032         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4033     optional = draw(one_of(none(), booleans()))
4034     _decoded = (
4035         draw(integers(min_value=0)),
4036         draw(integers(min_value=0)),
4037         draw(integers(min_value=0)),
4038     )
4039     return (value, expl, optional, _decoded)
4040
4041
4042 class AnyInherited(Any):
4043     pass
4044
4045
4046 class TestAny(CommonMixin, TestCase):
4047     base_klass = Any
4048
4049     def test_invalid_value_type(self):
4050         with self.assertRaises(InvalidValueType) as err:
4051             Any(123)
4052         repr(err.exception)
4053
4054     @given(booleans())
4055     def test_optional(self, optional):
4056         obj = Any(optional=optional)
4057         self.assertEqual(obj.optional, optional)
4058
4059     @given(binary())
4060     def test_ready(self, value):
4061         obj = Any()
4062         self.assertFalse(obj.ready)
4063         repr(obj)
4064         list(obj.pps())
4065         pprint(obj, big_blobs=True, with_decode_path=True)
4066         with self.assertRaises(ObjNotReady) as err:
4067             obj.encode()
4068         repr(err.exception)
4069         obj = Any(value)
4070         self.assertTrue(obj.ready)
4071         repr(obj)
4072         list(obj.pps())
4073         pprint(obj, big_blobs=True, with_decode_path=True)
4074
4075     @given(integers())
4076     def test_basic(self, value):
4077         integer_encoded = Integer(value).encode()
4078         for obj in (
4079                 Any(integer_encoded),
4080                 Any(Integer(value)),
4081                 Any(Any(Integer(value))),
4082         ):
4083             self.assertSequenceEqual(bytes(obj), integer_encoded)
4084             self.assertEqual(
4085                 obj.decode(obj.encode())[0].vlen,
4086                 len(integer_encoded),
4087             )
4088             repr(obj)
4089             list(obj.pps())
4090             pprint(obj, big_blobs=True, with_decode_path=True)
4091             self.assertSequenceEqual(obj.encode(), integer_encoded)
4092
4093     @given(binary(), binary())
4094     def test_comparison(self, value1, value2):
4095         for klass in (Any, AnyInherited):
4096             obj1 = klass(value1)
4097             obj2 = klass(value2)
4098             self.assertEqual(obj1 == obj2, value1 == value2)
4099             self.assertEqual(obj1 != obj2, value1 != value2)
4100             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
4101
4102     @given(data_strategy())
4103     def test_call(self, d):
4104         for klass in (Any, AnyInherited):
4105             (
4106                 value_initial,
4107                 expl_initial,
4108                 optional_initial,
4109                 _decoded_initial,
4110             ) = d.draw(any_values_strategy())
4111             obj_initial = klass(
4112                 value_initial,
4113                 expl_initial,
4114                 optional_initial or False,
4115                 _decoded_initial,
4116             )
4117             (
4118                 value,
4119                 expl,
4120                 optional,
4121                 _decoded,
4122             ) = d.draw(any_values_strategy(do_expl=True))
4123             obj = obj_initial(value, expl, optional)
4124             if obj.ready:
4125                 value_expected = None if value is None else value
4126                 self.assertEqual(obj, value_expected)
4127             self.assertEqual(obj.expl_tag, expl or expl_initial)
4128             if obj.default is None:
4129                 optional = optional_initial if optional is None else optional
4130                 optional = False if optional is None else optional
4131             self.assertEqual(obj.optional, optional)
4132
4133     def test_simultaneous_impl_expl(self):
4134         # override it, as Any does not have implicit tag
4135         pass
4136
4137     def test_decoded(self):
4138         # override it, as Any does not have implicit tag
4139         pass
4140
4141     @given(any_values_strategy())
4142     def test_copy(self, values):
4143         for klass in (Any, AnyInherited):
4144             obj = klass(*values)
4145             obj_copied = obj.copy()
4146             self.assert_copied_basic_fields(obj, obj_copied)
4147             self.assertEqual(obj._value, obj_copied._value)
4148
4149     @given(binary().map(OctetString))
4150     def test_stripped(self, value):
4151         obj = Any(value)
4152         with self.assertRaises(NotEnoughData):
4153             obj.decode(obj.encode()[:-1])
4154
4155     @given(
4156         binary(),
4157         integers(min_value=1).map(tag_ctxc),
4158     )
4159     def test_stripped_expl(self, value, tag_expl):
4160         obj = Any(value, expl=tag_expl)
4161         with self.assertRaises(NotEnoughData):
4162             obj.decode(obj.encode()[:-1])
4163
4164     @given(
4165         integers(min_value=31),
4166         integers(min_value=0),
4167         decode_path_strat,
4168     )
4169     def test_bad_tag(self, tag, offset, decode_path):
4170         with self.assertRaises(DecodeError) as err:
4171             Any().decode(
4172                 tag_encode(tag)[:-1],
4173                 offset=offset,
4174                 decode_path=decode_path,
4175             )
4176         repr(err.exception)
4177         self.assertEqual(err.exception.offset, offset)
4178         self.assertEqual(err.exception.decode_path, decode_path)
4179
4180     @given(
4181         integers(min_value=128),
4182         integers(min_value=0),
4183         decode_path_strat,
4184     )
4185     def test_bad_len(self, l, offset, decode_path):
4186         with self.assertRaises(DecodeError) as err:
4187             Any().decode(
4188                 Any.tag_default + len_encode(l)[:-1],
4189                 offset=offset,
4190                 decode_path=decode_path,
4191             )
4192         repr(err.exception)
4193         self.assertEqual(err.exception.offset, offset)
4194         self.assertEqual(err.exception.decode_path, decode_path)
4195
4196     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4197     @given(
4198         any_values_strategy(),
4199         integers().map(lambda x: Integer(x).encode()),
4200         integers(min_value=1).map(tag_ctxc),
4201         integers(min_value=0),
4202         binary(max_size=5),
4203     )
4204     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
4205         for klass in (Any, AnyInherited):
4206             _, _, optional, _decoded = values
4207             obj = klass(value=value, optional=optional, _decoded=_decoded)
4208             repr(obj)
4209             list(obj.pps())
4210             pprint(obj, big_blobs=True, with_decode_path=True)
4211             self.assertFalse(obj.expled)
4212             obj_encoded = obj.encode()
4213             obj_expled = obj(value, expl=tag_expl)
4214             self.assertTrue(obj_expled.expled)
4215             repr(obj_expled)
4216             list(obj_expled.pps())
4217             pprint(obj_expled, big_blobs=True, with_decode_path=True)
4218             obj_expled_encoded = obj_expled.encode()
4219             ctx_copied = deepcopy(ctx_dummy)
4220             obj_decoded, tail = obj_expled.decode(
4221                 obj_expled_encoded + tail_junk,
4222                 offset=offset,
4223                 ctx=ctx_copied,
4224             )
4225             self.assertDictEqual(ctx_copied, ctx_dummy)
4226             repr(obj_decoded)
4227             list(obj_decoded.pps())
4228             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4229             self.assertEqual(tail, tail_junk)
4230             self.assertEqual(obj_decoded, obj_expled)
4231             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
4232             self.assertEqual(bytes(obj_decoded), bytes(obj))
4233             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4234             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4235             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4236             self.assertEqual(
4237                 obj_decoded.expl_llen,
4238                 len(len_encode(len(obj_encoded))),
4239             )
4240             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4241             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4242             self.assertEqual(
4243                 obj_decoded.offset,
4244                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4245             )
4246             self.assertEqual(obj_decoded.expl_offset, offset)
4247             self.assertEqual(obj_decoded.tlen, 0)
4248             self.assertEqual(obj_decoded.llen, 0)
4249             self.assertEqual(obj_decoded.vlen, len(value))
4250
4251     @given(
4252         integers(min_value=1).map(tag_ctxc),
4253         integers(min_value=0, max_value=3),
4254         integers(min_value=0),
4255         decode_path_strat,
4256         binary(),
4257     )
4258     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
4259         chunk = Boolean(False, expl=expl).encode()
4260         encoded = (
4261             OctetString.tag_default +
4262             LENINDEF +
4263             b"".join([chunk] * chunks) +
4264             EOC
4265         )
4266         with self.assertRaises(LenIndefForm):
4267             Any().decode(
4268                 encoded + junk,
4269                 offset=offset,
4270                 decode_path=decode_path,
4271             )
4272         obj, tail = Any().decode(
4273             encoded + junk,
4274             offset=offset,
4275             decode_path=decode_path,
4276             ctx={"bered": True},
4277         )
4278         self.assertSequenceEqual(tail, junk)
4279         self.assertEqual(obj.offset, offset)
4280         self.assertEqual(obj.tlvlen, len(encoded))
4281         self.assertTrue(obj.lenindef)
4282         self.assertFalse(obj.ber_encoded)
4283         self.assertTrue(obj.bered)
4284         obj = obj.copy()
4285         self.assertTrue(obj.lenindef)
4286         self.assertFalse(obj.ber_encoded)
4287         self.assertTrue(obj.bered)
4288         repr(obj)
4289         list(obj.pps())
4290         pprint(obj, big_blobs=True, with_decode_path=True)
4291         with self.assertRaises(NotEnoughData) as err:
4292             Any().decode(
4293                 encoded[:-1],
4294                 offset=offset,
4295                 decode_path=decode_path,
4296                 ctx={"bered": True},
4297             )
4298         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
4299         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
4300
4301         class SeqOf(SequenceOf):
4302             schema = Boolean(expl=expl)
4303
4304         class Seq(Sequence):
4305             schema = (
4306                 ("type", ObjectIdentifier(defines=((("value",), {
4307                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
4308                 }),))),
4309                 ("value", Any()),
4310             )
4311         seq = Seq((
4312             ("type", ObjectIdentifier("1.2.3")),
4313             ("value", Any(encoded)),
4314         ))
4315         seq_encoded = seq.encode()
4316         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
4317         self.assertIsNotNone(seq_decoded["value"].defined)
4318         repr(seq_decoded)
4319         list(seq_decoded.pps())
4320         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
4321         self.assertTrue(seq_decoded.bered)
4322         self.assertFalse(seq_decoded["type"].bered)
4323         self.assertTrue(seq_decoded["value"].bered)
4324
4325         chunk = chunk[:-1] + b"\x01"
4326         chunks = b"".join([chunk] * (chunks + 1))
4327         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
4328         seq = Seq((
4329             ("type", ObjectIdentifier("1.2.3")),
4330             ("value", Any(encoded)),
4331         ))
4332         seq_encoded = seq.encode()
4333         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
4334         self.assertIsNotNone(seq_decoded["value"].defined)
4335         repr(seq_decoded)
4336         list(seq_decoded.pps())
4337         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
4338         self.assertTrue(seq_decoded.bered)
4339         self.assertFalse(seq_decoded["type"].bered)
4340         self.assertTrue(seq_decoded["value"].bered)
4341
4342
4343 @composite
4344 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
4345     if schema is None:
4346         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
4347         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
4348             one_of(
4349                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
4350                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
4351             ),
4352             min_size=len(names),
4353             max_size=len(names),
4354         ))]
4355         schema = [
4356             (name, Integer(**tag_kwargs))
4357             for name, tag_kwargs in zip(names, tags)
4358         ]
4359     value = None
4360     if value_required or draw(booleans()):
4361         value = draw(tuples(
4362             sampled_from([name for name, _ in schema]),
4363             integers().map(Integer),
4364         ))
4365     expl = None
4366     if do_expl:
4367         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4368     default = draw(one_of(
4369         none(),
4370         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
4371     ))
4372     optional = draw(one_of(none(), booleans()))
4373     _decoded = (
4374         draw(integers(min_value=0)),
4375         draw(integers(min_value=0)),
4376         draw(integers(min_value=0)),
4377     )
4378     return (schema, value, expl, default, optional, _decoded)
4379
4380
4381 class ChoiceInherited(Choice):
4382     pass
4383
4384
4385 class TestChoice(CommonMixin, TestCase):
4386     class Wahl(Choice):
4387         schema = (("whatever", Boolean()),)
4388     base_klass = Wahl
4389
4390     def test_schema_required(self):
4391         with assertRaisesRegex(self, ValueError, "schema must be specified"):
4392             Choice()
4393
4394     def test_impl_forbidden(self):
4395         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
4396             Choice(impl=b"whatever")
4397
4398     def test_invalid_value_type(self):
4399         with self.assertRaises(InvalidValueType) as err:
4400             self.base_klass(123)
4401         repr(err.exception)
4402         with self.assertRaises(ObjUnknown) as err:
4403             self.base_klass(("whenever", Boolean(False)))
4404         repr(err.exception)
4405         with self.assertRaises(InvalidValueType) as err:
4406             self.base_klass(("whatever", Integer(123)))
4407         repr(err.exception)
4408
4409     @given(booleans())
4410     def test_optional(self, optional):
4411         obj = self.base_klass(
4412             default=self.base_klass(("whatever", Boolean(False))),
4413             optional=optional,
4414         )
4415         self.assertTrue(obj.optional)
4416
4417     @given(booleans())
4418     def test_ready(self, value):
4419         obj = self.base_klass()
4420         self.assertFalse(obj.ready)
4421         repr(obj)
4422         list(obj.pps())
4423         pprint(obj, big_blobs=True, with_decode_path=True)
4424         self.assertIsNone(obj["whatever"])
4425         with self.assertRaises(ObjNotReady) as err:
4426             obj.encode()
4427         repr(err.exception)
4428         obj["whatever"] = Boolean()
4429         self.assertFalse(obj.ready)
4430         repr(obj)
4431         list(obj.pps())
4432         pprint(obj, big_blobs=True, with_decode_path=True)
4433         obj["whatever"] = Boolean(value)
4434         self.assertTrue(obj.ready)
4435         repr(obj)
4436         list(obj.pps())
4437         pprint(obj, big_blobs=True, with_decode_path=True)
4438
4439     @given(booleans(), booleans())
4440     def test_comparison(self, value1, value2):
4441         class WahlInherited(self.base_klass):
4442             pass
4443         for klass in (self.base_klass, WahlInherited):
4444             obj1 = klass(("whatever", Boolean(value1)))
4445             obj2 = klass(("whatever", Boolean(value2)))
4446             self.assertEqual(obj1 == obj2, value1 == value2)
4447             self.assertEqual(obj1 != obj2, value1 != value2)
4448             self.assertEqual(obj1 == obj2._value, value1 == value2)
4449             self.assertFalse(obj1 == obj2._value[1])
4450
4451     @given(data_strategy())
4452     def test_call(self, d):
4453         for klass in (Choice, ChoiceInherited):
4454             (
4455                 schema_initial,
4456                 value_initial,
4457                 expl_initial,
4458                 default_initial,
4459                 optional_initial,
4460                 _decoded_initial,
4461             ) = d.draw(choice_values_strategy())
4462
4463             class Wahl(klass):
4464                 schema = schema_initial
4465             obj_initial = Wahl(
4466                 value=value_initial,
4467                 expl=expl_initial,
4468                 default=default_initial,
4469                 optional=optional_initial or False,
4470                 _decoded=_decoded_initial,
4471             )
4472             (
4473                 _,
4474                 value,
4475                 expl,
4476                 default,
4477                 optional,
4478                 _decoded,
4479             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
4480             obj = obj_initial(value, expl, default, optional)
4481             if obj.ready:
4482                 value_expected = default if value is None else value
4483                 value_expected = (
4484                     default_initial if value_expected is None
4485                     else value_expected
4486                 )
4487                 self.assertEqual(obj.choice, value_expected[0])
4488                 self.assertEqual(obj.value, int(value_expected[1]))
4489             self.assertEqual(obj.expl_tag, expl or expl_initial)
4490             default_expect = default_initial if default is None else default
4491             if default_expect is not None:
4492                 self.assertEqual(obj.default.choice, default_expect[0])
4493                 self.assertEqual(obj.default.value, int(default_expect[1]))
4494             if obj.default is None:
4495                 optional = optional_initial if optional is None else optional
4496                 optional = False if optional is None else optional
4497             else:
4498                 optional = True
4499             self.assertEqual(obj.optional, optional)
4500             self.assertEqual(obj.specs, obj_initial.specs)
4501
4502     def test_simultaneous_impl_expl(self):
4503         # override it, as Any does not have implicit tag
4504         pass
4505
4506     def test_decoded(self):
4507         # override it, as Any does not have implicit tag
4508         pass
4509
4510     @given(choice_values_strategy())
4511     def test_copy(self, values):
4512         _schema, value, expl, default, optional, _decoded = values
4513
4514         class Wahl(self.base_klass):
4515             schema = _schema
4516         obj = Wahl(
4517             value=value,
4518             expl=expl,
4519             default=default,
4520             optional=optional or False,
4521             _decoded=_decoded,
4522         )
4523         obj_copied = obj.copy()
4524         self.assertIsNone(obj.tag)
4525         self.assertIsNone(obj_copied.tag)
4526         # hack for assert_copied_basic_fields
4527         obj.tag = "whatever"
4528         obj_copied.tag = "whatever"
4529         self.assert_copied_basic_fields(obj, obj_copied)
4530         self.assertEqual(obj._value, obj_copied._value)
4531         self.assertEqual(obj.specs, obj_copied.specs)
4532
4533     @given(booleans())
4534     def test_stripped(self, value):
4535         obj = self.base_klass(("whatever", Boolean(value)))
4536         with self.assertRaises(NotEnoughData):
4537             obj.decode(obj.encode()[:-1])
4538
4539     @given(
4540         booleans(),
4541         integers(min_value=1).map(tag_ctxc),
4542     )
4543     def test_stripped_expl(self, value, tag_expl):
4544         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
4545         with self.assertRaises(NotEnoughData):
4546             obj.decode(obj.encode()[:-1])
4547
4548     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4549     @given(data_strategy())
4550     def test_symmetric(self, d):
4551         _schema, value, _, default, optional, _decoded = d.draw(
4552             choice_values_strategy(value_required=True)
4553         )
4554         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4555         offset = d.draw(integers(min_value=0))
4556         tail_junk = d.draw(binary(max_size=5))
4557
4558         class Wahl(self.base_klass):
4559             schema = _schema
4560         obj = Wahl(
4561             value=value,
4562             default=default,
4563             optional=optional,
4564             _decoded=_decoded,
4565         )
4566         repr(obj)
4567         list(obj.pps())
4568         pprint(obj, big_blobs=True, with_decode_path=True)
4569         self.assertFalse(obj.expled)
4570         obj_encoded = obj.encode()
4571         obj_expled = obj(value, expl=tag_expl)
4572         self.assertTrue(obj_expled.expled)
4573         repr(obj_expled)
4574         list(obj_expled.pps())
4575         pprint(obj_expled, big_blobs=True, with_decode_path=True)
4576         obj_expled_encoded = obj_expled.encode()
4577         ctx_copied = deepcopy(ctx_dummy)
4578         obj_decoded, tail = obj_expled.decode(
4579             obj_expled_encoded + tail_junk,
4580             offset=offset,
4581             ctx=ctx_copied,
4582         )
4583         self.assertDictEqual(ctx_copied, ctx_dummy)
4584         repr(obj_decoded)
4585         list(obj_decoded.pps())
4586         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4587         self.assertEqual(tail, tail_junk)
4588         self.assertEqual(obj_decoded, obj_expled)
4589         self.assertEqual(obj_decoded.choice, obj_expled.choice)
4590         self.assertEqual(obj_decoded.value, obj_expled.value)
4591         self.assertEqual(obj_decoded.choice, obj.choice)
4592         self.assertEqual(obj_decoded.value, obj.value)
4593         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4594         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4595         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4596         self.assertEqual(
4597             obj_decoded.expl_llen,
4598             len(len_encode(len(obj_encoded))),
4599         )
4600         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4601         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4602         self.assertEqual(
4603             obj_decoded.offset,
4604             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4605         )
4606         self.assertEqual(obj_decoded.expl_offset, offset)
4607         self.assertSequenceEqual(
4608             obj_expled_encoded[
4609                 obj_decoded.value.fulloffset - offset:
4610                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
4611             ],
4612             obj_encoded,
4613         )
4614
4615     @given(integers())
4616     def test_set_get(self, value):
4617         class Wahl(Choice):
4618             schema = (
4619                 ("erste", Boolean()),
4620                 ("zweite", Integer()),
4621             )
4622         obj = Wahl()
4623         with self.assertRaises(ObjUnknown) as err:
4624             obj["whatever"] = "whenever"
4625         with self.assertRaises(InvalidValueType) as err:
4626             obj["zweite"] = Boolean(False)
4627         obj["zweite"] = Integer(value)
4628         repr(err.exception)
4629         with self.assertRaises(ObjUnknown) as err:
4630             obj["whatever"]
4631         repr(err.exception)
4632         self.assertIsNone(obj["erste"])
4633         self.assertEqual(obj["zweite"], Integer(value))
4634
4635     def test_tag_mismatch(self):
4636         class Wahl(Choice):
4637             schema = (
4638                 ("erste", Boolean()),
4639             )
4640         int_encoded = Integer(123).encode()
4641         bool_encoded = Boolean(False).encode()
4642         obj = Wahl()
4643         obj.decode(bool_encoded)
4644         with self.assertRaises(TagMismatch):
4645             obj.decode(int_encoded)
4646
4647     def test_tag_mismatch_underlying(self):
4648         class SeqOfBoolean(SequenceOf):
4649             schema = Boolean()
4650
4651         class SeqOfInteger(SequenceOf):
4652             schema = Integer()
4653
4654         class Wahl(Choice):
4655             schema = (
4656                 ("erste", SeqOfBoolean()),
4657             )
4658
4659         int_encoded = SeqOfInteger((Integer(123),)).encode()
4660         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
4661         obj = Wahl()
4662         obj.decode(bool_encoded)
4663         with self.assertRaises(TagMismatch) as err:
4664             obj.decode(int_encoded)
4665         self.assertEqual(err.exception.decode_path, ("erste", "0"))
4666
4667
4668 @composite
4669 def seq_values_strategy(draw, seq_klass, do_expl=False):
4670     value = None
4671     if draw(booleans()):
4672         value = seq_klass()
4673         value._value = {
4674             k: v for k, v in draw(dictionaries(
4675                 integers(),
4676                 one_of(
4677                     booleans().map(Boolean),
4678                     integers().map(Integer),
4679                 ),
4680             )).items()
4681         }
4682     schema = None
4683     if draw(booleans()):
4684         schema = list(draw(dictionaries(
4685             integers(),
4686             one_of(
4687                 booleans().map(Boolean),
4688                 integers().map(Integer),
4689             ),
4690         )).items())
4691     impl = None
4692     expl = None
4693     if do_expl:
4694         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4695     else:
4696         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4697     default = None
4698     if draw(booleans()):
4699         default = seq_klass()
4700         default._value = {
4701             k: v for k, v in draw(dictionaries(
4702                 integers(),
4703                 one_of(
4704                     booleans().map(Boolean),
4705                     integers().map(Integer),
4706                 ),
4707             )).items()
4708         }
4709     optional = draw(one_of(none(), booleans()))
4710     _decoded = (
4711         draw(integers(min_value=0)),
4712         draw(integers(min_value=0)),
4713         draw(integers(min_value=0)),
4714     )
4715     return (value, schema, impl, expl, default, optional, _decoded)
4716
4717
4718 @composite
4719 def sequence_strategy(draw, seq_klass):
4720     inputs = draw(lists(
4721         one_of(
4722             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
4723             tuples(just(Integer), integers(), one_of(none(), integers())),
4724         ),
4725         max_size=6,
4726     ))
4727     tags = draw(sets(
4728         integers(min_value=1),
4729         min_size=len(inputs),
4730         max_size=len(inputs),
4731     ))
4732     inits = [
4733         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4734         for tag, expled in zip(tags, draw(lists(
4735             booleans(),
4736             min_size=len(inputs),
4737             max_size=len(inputs),
4738         )))
4739     ]
4740     empties = []
4741     for i, optional in enumerate(draw(lists(
4742             sampled_from(("required", "optional", "empty")),
4743             min_size=len(inputs),
4744             max_size=len(inputs),
4745     ))):
4746         if optional in ("optional", "empty"):
4747             inits[i]["optional"] = True
4748         if optional == "empty":
4749             empties.append(i)
4750     empties = set(empties)
4751     names = list(draw(sets(
4752         text_printable,
4753         min_size=len(inputs),
4754         max_size=len(inputs),
4755     )))
4756     schema = []
4757     for i, (klass, value, default) in enumerate(inputs):
4758         schema.append((names[i], klass(default=default, **inits[i])))
4759     seq_name = draw(text_letters())
4760     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4761     seq = Seq()
4762     expects = []
4763     for i, (klass, value, default) in enumerate(inputs):
4764         name = names[i]
4765         _, spec = schema[i]
4766         expect = {
4767             "name": name,
4768             "optional": False,
4769             "presented": False,
4770             "default_value": None if spec.default is None else default,
4771             "value": None,
4772         }
4773         if i in empties:
4774             expect["optional"] = True
4775         else:
4776             expect["presented"] = True
4777             expect["value"] = value
4778             if spec.optional:
4779                 expect["optional"] = True
4780             if default is not None and default == value:
4781                 expect["presented"] = False
4782             seq[name] = klass(value)
4783         expects.append(expect)
4784     return seq, expects
4785
4786
4787 @composite
4788 def sequences_strategy(draw, seq_klass):
4789     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
4790     inits = [
4791         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4792         for tag, expled in zip(tags, draw(lists(
4793             booleans(),
4794             min_size=len(tags),
4795             max_size=len(tags),
4796         )))
4797     ]
4798     defaulted = set(
4799         i for i, is_default in enumerate(draw(lists(
4800             booleans(),
4801             min_size=len(tags),
4802             max_size=len(tags),
4803         ))) if is_default
4804     )
4805     names = list(draw(sets(
4806         text_printable,
4807         min_size=len(tags),
4808         max_size=len(tags),
4809     )))
4810     seq_expectses = draw(lists(
4811         sequence_strategy(seq_klass=seq_klass),
4812         min_size=len(tags),
4813         max_size=len(tags),
4814     ))
4815     seqs = [seq for seq, _ in seq_expectses]
4816     schema = []
4817     for i, (name, seq) in enumerate(zip(names, seqs)):
4818         schema.append((
4819             name,
4820             seq(default=(seq if i in defaulted else None), **inits[i]),
4821         ))
4822     seq_name = draw(text_letters())
4823     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4824     seq_outer = Seq()
4825     expect_outers = []
4826     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
4827         expect = {
4828             "name": name,
4829             "expects": expects_inner,
4830             "presented": False,
4831         }
4832         seq_outer[name] = seq_inner
4833         if seq_outer.specs[name].default is None:
4834             expect["presented"] = True
4835         expect_outers.append(expect)
4836     return seq_outer, expect_outers
4837
4838
4839 class SeqMixing(object):
4840     def test_invalid_value_type(self):
4841         with self.assertRaises(InvalidValueType) as err:
4842             self.base_klass(123)
4843         repr(err.exception)
4844
4845     def test_invalid_value_type_set(self):
4846         class Seq(self.base_klass):
4847             schema = (("whatever", Boolean()),)
4848         seq = Seq()
4849         with self.assertRaises(InvalidValueType) as err:
4850             seq["whatever"] = Integer(123)
4851         repr(err.exception)
4852
4853     @given(booleans())
4854     def test_optional(self, optional):
4855         obj = self.base_klass(default=self.base_klass(), optional=optional)
4856         self.assertTrue(obj.optional)
4857
4858     @given(data_strategy())
4859     def test_ready(self, d):
4860         ready = {
4861             str(i): v for i, v in enumerate(d.draw(lists(
4862                 booleans(),
4863                 min_size=1,
4864                 max_size=3,
4865             )))
4866         }
4867         non_ready = {
4868             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
4869                 booleans(),
4870                 min_size=1,
4871                 max_size=3,
4872             )))
4873         }
4874         schema_input = []
4875         for name in d.draw(permutations(
4876                 list(ready.keys()) + list(non_ready.keys()),
4877         )):
4878             schema_input.append((name, Boolean()))
4879
4880         class Seq(self.base_klass):
4881             schema = tuple(schema_input)
4882         seq = Seq()
4883         for name in ready.keys():
4884             seq[name]
4885             seq[name] = Boolean()
4886         self.assertFalse(seq.ready)
4887         repr(seq)
4888         list(seq.pps())
4889         pprint(seq, big_blobs=True, with_decode_path=True)
4890         for name, value in ready.items():
4891             seq[name] = Boolean(value)
4892         self.assertFalse(seq.ready)
4893         repr(seq)
4894         list(seq.pps())
4895         pprint(seq, big_blobs=True, with_decode_path=True)
4896         with self.assertRaises(ObjNotReady) as err:
4897             seq.encode()
4898         repr(err.exception)
4899         for name, value in non_ready.items():
4900             seq[name] = Boolean(value)
4901         self.assertTrue(seq.ready)
4902         repr(seq)
4903         list(seq.pps())
4904         pprint(seq, big_blobs=True, with_decode_path=True)
4905
4906     @given(data_strategy())
4907     def test_call(self, d):
4908         class SeqInherited(self.base_klass):
4909             pass
4910         for klass in (self.base_klass, SeqInherited):
4911             (
4912                 value_initial,
4913                 schema_initial,
4914                 impl_initial,
4915                 expl_initial,
4916                 default_initial,
4917                 optional_initial,
4918                 _decoded_initial,
4919             ) = d.draw(seq_values_strategy(seq_klass=klass))
4920             obj_initial = klass(
4921                 value_initial,
4922                 schema_initial,
4923                 impl_initial,
4924                 expl_initial,
4925                 default_initial,
4926                 optional_initial or False,
4927                 _decoded_initial,
4928             )
4929             (
4930                 value,
4931                 _,
4932                 impl,
4933                 expl,
4934                 default,
4935                 optional,
4936                 _decoded,
4937             ) = d.draw(seq_values_strategy(
4938                 seq_klass=klass,
4939                 do_expl=impl_initial is None,
4940             ))
4941             obj = obj_initial(value, impl, expl, default, optional)
4942             value_expected = default if value is None else value
4943             value_expected = (
4944                 default_initial if value_expected is None
4945                 else value_expected
4946             )
4947             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
4948             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4949             self.assertEqual(obj.expl_tag, expl or expl_initial)
4950             self.assertEqual(
4951                 {} if obj.default is None else obj.default._value,
4952                 getattr(default_initial if default is None else default, "_value", {}),
4953             )
4954             if obj.default is None:
4955                 optional = optional_initial if optional is None else optional
4956                 optional = False if optional is None else optional
4957             else:
4958                 optional = True
4959             self.assertEqual(list(obj.specs.items()), schema_initial or [])
4960             self.assertEqual(obj.optional, optional)
4961
4962     @given(data_strategy())
4963     def test_copy(self, d):
4964         class SeqInherited(self.base_klass):
4965             pass
4966         for klass in (self.base_klass, SeqInherited):
4967             values = d.draw(seq_values_strategy(seq_klass=klass))
4968             obj = klass(*values)
4969             obj_copied = obj.copy()
4970             self.assert_copied_basic_fields(obj, obj_copied)
4971             self.assertEqual(obj.specs, obj_copied.specs)
4972             self.assertEqual(obj._value, obj_copied._value)
4973
4974     @given(data_strategy())
4975     def test_stripped(self, d):
4976         value = d.draw(integers())
4977         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4978
4979         class Seq(self.base_klass):
4980             impl = tag_impl
4981             schema = (("whatever", Integer()),)
4982         seq = Seq()
4983         seq["whatever"] = Integer(value)
4984         with self.assertRaises(NotEnoughData):
4985             seq.decode(seq.encode()[:-1])
4986
4987     @given(data_strategy())
4988     def test_stripped_expl(self, d):
4989         value = d.draw(integers())
4990         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4991
4992         class Seq(self.base_klass):
4993             expl = tag_expl
4994             schema = (("whatever", Integer()),)
4995         seq = Seq()
4996         seq["whatever"] = Integer(value)
4997         with self.assertRaises(NotEnoughData):
4998             seq.decode(seq.encode()[:-1])
4999
5000     @given(binary(min_size=2))
5001     def test_non_tag_mismatch_raised(self, junk):
5002         try:
5003             _, _, len_encoded = tag_strip(memoryview(junk))
5004             len_decode(len_encoded)
5005         except Exception:
5006             assume(True)
5007         else:
5008             assume(False)
5009
5010         class Seq(self.base_klass):
5011             schema = (
5012                 ("whatever", Integer()),
5013                 ("junk", Any()),
5014                 ("whenever", Integer()),
5015             )
5016         seq = Seq()
5017         seq["whatever"] = Integer(123)
5018         seq["junk"] = Any(junk)
5019         seq["whenever"] = Integer(123)
5020         with self.assertRaises(DecodeError):
5021             seq.decode(seq.encode())
5022
5023     @given(
5024         integers(min_value=31),
5025         integers(min_value=0),
5026         decode_path_strat,
5027     )
5028     def test_bad_tag(self, tag, offset, decode_path):
5029         with self.assertRaises(DecodeError) as err:
5030             self.base_klass().decode(
5031                 tag_encode(tag)[:-1],
5032                 offset=offset,
5033                 decode_path=decode_path,
5034             )
5035         repr(err.exception)
5036         self.assertEqual(err.exception.offset, offset)
5037         self.assertEqual(err.exception.decode_path, decode_path)
5038
5039     @given(
5040         integers(min_value=128),
5041         integers(min_value=0),
5042         decode_path_strat,
5043     )
5044     def test_bad_len(self, l, offset, decode_path):
5045         with self.assertRaises(DecodeError) as err:
5046             self.base_klass().decode(
5047                 self.base_klass.tag_default + len_encode(l)[:-1],
5048                 offset=offset,
5049                 decode_path=decode_path,
5050             )
5051         repr(err.exception)
5052         self.assertEqual(err.exception.offset, offset)
5053         self.assertEqual(err.exception.decode_path, decode_path)
5054
5055     def _assert_expects(self, seq, expects):
5056         for expect in expects:
5057             self.assertEqual(
5058                 seq.specs[expect["name"]].optional,
5059                 expect["optional"],
5060             )
5061             if expect["default_value"] is not None:
5062                 self.assertEqual(
5063                     seq.specs[expect["name"]].default,
5064                     expect["default_value"],
5065                 )
5066             if expect["presented"]:
5067                 self.assertIn(expect["name"], seq)
5068                 self.assertEqual(seq[expect["name"]], expect["value"])
5069
5070     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5071     @given(data_strategy())
5072     def test_symmetric(self, d):
5073         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
5074         tail_junk = d.draw(binary(max_size=5))
5075         self.assertTrue(seq.ready)
5076         self.assertFalse(seq.decoded)
5077         self._assert_expects(seq, expects)
5078         repr(seq)
5079         list(seq.pps())
5080         pprint(seq, big_blobs=True, with_decode_path=True)
5081         self.assertTrue(seq.ready)
5082         seq_encoded = seq.encode()
5083         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
5084         self.assertFalse(seq_decoded.lenindef)
5085         self.assertFalse(seq_decoded.ber_encoded)
5086         self.assertFalse(seq_decoded.bered)
5087
5088         t, _, lv = tag_strip(seq_encoded)
5089         _, _, v = len_decode(lv)
5090         seq_encoded_lenindef = t + LENINDEF + v + EOC
5091         ctx_copied = deepcopy(ctx_dummy)
5092         ctx_copied["bered"] = True
5093         seq_decoded_lenindef, tail_lenindef = seq.decode(
5094             seq_encoded_lenindef + tail_junk,
5095             ctx=ctx_copied,
5096         )
5097         del ctx_copied["bered"]
5098         self.assertDictEqual(ctx_copied, ctx_dummy)
5099         self.assertTrue(seq_decoded_lenindef.lenindef)
5100         self.assertTrue(seq_decoded_lenindef.bered)
5101         seq_decoded_lenindef = seq_decoded_lenindef.copy()
5102         self.assertTrue(seq_decoded_lenindef.lenindef)
5103         self.assertTrue(seq_decoded_lenindef.bered)
5104         with self.assertRaises(DecodeError):
5105             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
5106         with self.assertRaises(DecodeError):
5107             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
5108         repr(seq_decoded_lenindef)
5109         list(seq_decoded_lenindef.pps())
5110         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
5111         self.assertTrue(seq_decoded_lenindef.ready)
5112
5113         for decoded, decoded_tail, encoded in (
5114                 (seq_decoded, tail, seq_encoded),
5115                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
5116         ):
5117             self.assertEqual(decoded_tail, tail_junk)
5118             self._assert_expects(decoded, expects)
5119             self.assertEqual(seq, decoded)
5120             self.assertEqual(decoded.encode(), seq_encoded)
5121             self.assertEqual(decoded.tlvlen, len(encoded))
5122             for expect in expects:
5123                 if not expect["presented"]:
5124                     self.assertNotIn(expect["name"], decoded)
5125                     continue
5126                 self.assertIn(expect["name"], decoded)
5127                 obj = decoded[expect["name"]]
5128                 self.assertTrue(obj.decoded)
5129                 offset = obj.expl_offset if obj.expled else obj.offset
5130                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
5131                 self.assertSequenceEqual(
5132                     seq_encoded[offset:offset + tlvlen],
5133                     obj.encode(),
5134                 )
5135
5136     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5137     @given(data_strategy())
5138     def test_symmetric_with_seq(self, d):
5139         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
5140         self.assertTrue(seq.ready)
5141         seq_encoded = seq.encode()
5142         seq_decoded, tail = seq.decode(seq_encoded)
5143         self.assertEqual(tail, b"")
5144         self.assertTrue(seq.ready)
5145         self.assertEqual(seq, seq_decoded)
5146         self.assertEqual(seq_decoded.encode(), seq_encoded)
5147         for expect_outer in expect_outers:
5148             if not expect_outer["presented"]:
5149                 self.assertNotIn(expect_outer["name"], seq_decoded)
5150                 continue
5151             self.assertIn(expect_outer["name"], seq_decoded)
5152             obj = seq_decoded[expect_outer["name"]]
5153             self.assertTrue(obj.decoded)
5154             offset = obj.expl_offset if obj.expled else obj.offset
5155             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
5156             self.assertSequenceEqual(
5157                 seq_encoded[offset:offset + tlvlen],
5158                 obj.encode(),
5159             )
5160             self._assert_expects(obj, expect_outer["expects"])
5161
5162     @given(data_strategy())
5163     def test_default_disappears(self, d):
5164         _schema = list(d.draw(dictionaries(
5165             text_letters(),
5166             sets(integers(), min_size=2, max_size=2),
5167             min_size=1,
5168         )).items())
5169
5170         class Seq(self.base_klass):
5171             schema = [
5172                 (n, Integer(default=d))
5173                 for n, (_, d) in _schema
5174             ]
5175         seq = Seq()
5176         for name, (value, _) in _schema:
5177             seq[name] = Integer(value)
5178         self.assertEqual(len(seq._value), len(_schema))
5179         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
5180         self.assertGreater(len(seq.encode()), len(empty_seq))
5181         for name, (_, default) in _schema:
5182             seq[name] = Integer(default)
5183         self.assertEqual(len(seq._value), 0)
5184         self.assertSequenceEqual(seq.encode(), empty_seq)
5185
5186     @given(data_strategy())
5187     def test_encoded_default_not_accepted(self, d):
5188         _schema = list(d.draw(dictionaries(
5189             text_letters(),
5190             integers(),
5191             min_size=1,
5192         )).items())
5193         tags = [tag_encode(tag) for tag in d.draw(sets(
5194             integers(min_value=0),
5195             min_size=len(_schema),
5196             max_size=len(_schema),
5197         ))]
5198
5199         class SeqWithoutDefault(self.base_klass):
5200             schema = [
5201                 (n, Integer(impl=t))
5202                 for (n, _), t in zip(_schema, tags)
5203             ]
5204         seq_without_default = SeqWithoutDefault()
5205         for name, value in _schema:
5206             seq_without_default[name] = Integer(value)
5207         seq_encoded = seq_without_default.encode()
5208
5209         class SeqWithDefault(self.base_klass):
5210             schema = [
5211                 (n, Integer(default=v, impl=t))
5212                 for (n, v), t in zip(_schema, tags)
5213             ]
5214         seq_with_default = SeqWithDefault()
5215         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5216             seq_with_default.decode(seq_encoded)
5217         for ctx in ({"bered": True}, {"allow_default_values": True}):
5218             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
5219             self.assertTrue(seq_decoded.ber_encoded)
5220             self.assertTrue(seq_decoded.bered)
5221             seq_decoded = seq_decoded.copy()
5222             self.assertTrue(seq_decoded.ber_encoded)
5223             self.assertTrue(seq_decoded.bered)
5224             for name, value in _schema:
5225                 self.assertEqual(seq_decoded[name], seq_with_default[name])
5226                 self.assertEqual(seq_decoded[name], value)
5227
5228     @given(data_strategy())
5229     def test_missing_from_spec(self, d):
5230         names = list(d.draw(sets(text_letters(), min_size=2)))
5231         tags = [tag_encode(tag) for tag in d.draw(sets(
5232             integers(min_value=0),
5233             min_size=len(names),
5234             max_size=len(names),
5235         ))]
5236         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
5237
5238         class SeqFull(self.base_klass):
5239             schema = [(n, Integer(impl=t)) for n, t in names_tags]
5240         seq_full = SeqFull()
5241         for i, name in enumerate(names):
5242             seq_full[name] = Integer(i)
5243         seq_encoded = seq_full.encode()
5244         altered = names_tags[:-2] + names_tags[-1:]
5245
5246         class SeqMissing(self.base_klass):
5247             schema = [(n, Integer(impl=t)) for n, t in altered]
5248         seq_missing = SeqMissing()
5249         with self.assertRaises(TagMismatch):
5250             seq_missing.decode(seq_encoded)
5251
5252     @given(data_strategy())
5253     def test_bered(self, d):
5254         class Seq(self.base_klass):
5255             schema = (("underlying", Boolean()),)
5256         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
5257         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
5258         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
5259         self.assertFalse(decoded.ber_encoded)
5260         self.assertFalse(decoded.lenindef)
5261         self.assertTrue(decoded.bered)
5262         decoded = decoded.copy()
5263         self.assertFalse(decoded.ber_encoded)
5264         self.assertFalse(decoded.lenindef)
5265         self.assertTrue(decoded.bered)
5266
5267         class Seq(self.base_klass):
5268             schema = (("underlying", OctetString()),)
5269         encoded = (
5270             tag_encode(form=TagFormConstructed, num=4) +
5271             LENINDEF +
5272             OctetString(b"whatever").encode() +
5273             EOC
5274         )
5275         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
5276         with self.assertRaises(DecodeError):
5277             Seq().decode(encoded)
5278         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
5279         self.assertFalse(decoded.ber_encoded)
5280         self.assertFalse(decoded.lenindef)
5281         self.assertTrue(decoded.bered)
5282         decoded = decoded.copy()
5283         self.assertFalse(decoded.ber_encoded)
5284         self.assertFalse(decoded.lenindef)
5285         self.assertTrue(decoded.bered)
5286
5287
5288 class TestSequence(SeqMixing, CommonMixin, TestCase):
5289     base_klass = Sequence
5290
5291     @given(
5292         integers(),
5293         binary(min_size=1),
5294     )
5295     def test_remaining(self, value, junk):
5296         class Seq(Sequence):
5297             schema = (
5298                 ("whatever", Integer()),
5299             )
5300         int_encoded = Integer(value).encode()
5301         junked = b"".join((
5302             Sequence.tag_default,
5303             len_encode(len(int_encoded + junk)),
5304             int_encoded + junk,
5305         ))
5306         with assertRaisesRegex(self, DecodeError, "remaining"):
5307             Seq().decode(junked)
5308
5309     @given(sets(text_letters(), min_size=2))
5310     def test_obj_unknown(self, names):
5311         missing = names.pop()
5312
5313         class Seq(Sequence):
5314             schema = [(n, Boolean()) for n in names]
5315         seq = Seq()
5316         with self.assertRaises(ObjUnknown) as err:
5317             seq[missing]
5318         repr(err.exception)
5319         with self.assertRaises(ObjUnknown) as err:
5320             seq[missing] = Boolean()
5321         repr(err.exception)
5322
5323     def test_x690_vector(self):
5324         class Seq(Sequence):
5325             schema = (
5326                 ("name", IA5String()),
5327                 ("ok", Boolean()),
5328             )
5329         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
5330         self.assertEqual(seq["name"], "Smith")
5331         self.assertEqual(seq["ok"], True)
5332
5333
5334 class TestSet(SeqMixing, CommonMixin, TestCase):
5335     base_klass = Set
5336
5337     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5338     @given(data_strategy())
5339     def test_sorted(self, d):
5340         tags = [
5341             tag_encode(tag) for tag in
5342             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
5343         ]
5344
5345         class Seq(Set):
5346             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
5347         seq = Seq()
5348         for name, _ in Seq.schema:
5349             seq[name] = OctetString(b"")
5350         seq_encoded = seq.encode()
5351         seq_decoded, _ = seq.decode(seq_encoded)
5352         self.assertSequenceEqual(
5353             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5354             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
5355         )
5356
5357     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5358     @given(data_strategy())
5359     def test_unsorted(self, d):
5360         tags = [
5361             tag_encode(tag) for tag in
5362             d.draw(sets(integers(min_value=1), min_size=2, max_size=5))
5363         ]
5364         tags = d.draw(permutations(tags))
5365         assume(tags != sorted(tags))
5366         encoded = b"".join(OctetString(t, impl=t).encode() for t in tags)
5367         seq_encoded = b"".join((
5368             Set.tag_default,
5369             len_encode(len(encoded)),
5370             encoded,
5371         ))
5372
5373         class Seq(Set):
5374             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
5375         seq = Seq()
5376         with assertRaisesRegex(self, DecodeError, "unordered SET"):
5377             seq.decode(seq_encoded)
5378         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
5379             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
5380             self.assertTrue(seq_decoded.ber_encoded)
5381             self.assertTrue(seq_decoded.bered)
5382             seq_decoded = seq_decoded.copy()
5383             self.assertTrue(seq_decoded.ber_encoded)
5384             self.assertTrue(seq_decoded.bered)
5385             self.assertSequenceEqual(
5386                 [bytes(seq_decoded[str(i)]) for i, t in enumerate(tags)],
5387                 [t for t in tags],
5388             )
5389
5390
5391 @composite
5392 def seqof_values_strategy(draw, schema=None, do_expl=False):
5393     if schema is None:
5394         schema = draw(sampled_from((Boolean(), Integer())))
5395     bound_min, bound_max = sorted(draw(sets(
5396         integers(min_value=0, max_value=10),
5397         min_size=2,
5398         max_size=2,
5399     )))
5400     if isinstance(schema, Boolean):
5401         values_generator = booleans().map(Boolean)
5402     elif isinstance(schema, Integer):
5403         values_generator = integers().map(Integer)
5404     values_generator = lists(
5405         values_generator,
5406         min_size=bound_min,
5407         max_size=bound_max,
5408     )
5409     values = draw(one_of(none(), values_generator))
5410     impl = None
5411     expl = None
5412     if do_expl:
5413         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5414     else:
5415         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5416     default = draw(one_of(none(), values_generator))
5417     optional = draw(one_of(none(), booleans()))
5418     _decoded = (
5419         draw(integers(min_value=0)),
5420         draw(integers(min_value=0)),
5421         draw(integers(min_value=0)),
5422     )
5423     return (
5424         schema,
5425         values,
5426         (bound_min, bound_max),
5427         impl,
5428         expl,
5429         default,
5430         optional,
5431         _decoded,
5432     )
5433
5434
5435 class SeqOfMixing(object):
5436     def test_invalid_value_type(self):
5437         with self.assertRaises(InvalidValueType) as err:
5438             self.base_klass(123)
5439         repr(err.exception)
5440
5441     def test_invalid_values_type(self):
5442         class SeqOf(self.base_klass):
5443             schema = Integer()
5444         with self.assertRaises(InvalidValueType) as err:
5445             SeqOf([Integer(123), Boolean(False), Integer(234)])
5446         repr(err.exception)
5447
5448     def test_schema_required(self):
5449         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5450             self.base_klass.__mro__[1]()
5451
5452     @given(booleans(), booleans(), binary(), binary())
5453     def test_comparison(self, value1, value2, tag1, tag2):
5454         class SeqOf(self.base_klass):
5455             schema = Boolean()
5456         obj1 = SeqOf([Boolean(value1)])
5457         obj2 = SeqOf([Boolean(value2)])
5458         self.assertEqual(obj1 == obj2, value1 == value2)
5459         self.assertEqual(obj1 != obj2, value1 != value2)
5460         self.assertEqual(obj1 == list(obj2), value1 == value2)
5461         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
5462         obj1 = SeqOf([Boolean(value1)], impl=tag1)
5463         obj2 = SeqOf([Boolean(value1)], impl=tag2)
5464         self.assertEqual(obj1 == obj2, tag1 == tag2)
5465         self.assertEqual(obj1 != obj2, tag1 != tag2)
5466
5467     @given(lists(booleans()))
5468     def test_iter(self, values):
5469         class SeqOf(self.base_klass):
5470             schema = Boolean()
5471         obj = SeqOf([Boolean(value) for value in values])
5472         self.assertEqual(len(obj), len(values))
5473         for i, value in enumerate(obj):
5474             self.assertEqual(value, values[i])
5475
5476     @given(data_strategy())
5477     def test_ready(self, d):
5478         ready = [Integer(v) for v in d.draw(lists(
5479             integers(),
5480             min_size=1,
5481             max_size=3,
5482         ))]
5483         non_ready = [
5484             Integer() for _ in
5485             range(d.draw(integers(min_value=1, max_value=5)))
5486         ]
5487
5488         class SeqOf(self.base_klass):
5489             schema = Integer()
5490         values = d.draw(permutations(ready + non_ready))
5491         seqof = SeqOf()
5492         for value in values:
5493             seqof.append(value)
5494         self.assertFalse(seqof.ready)
5495         repr(seqof)
5496         list(seqof.pps())
5497         pprint(seqof, big_blobs=True, with_decode_path=True)
5498         with self.assertRaises(ObjNotReady) as err:
5499             seqof.encode()
5500         repr(err.exception)
5501         for i, value in enumerate(values):
5502             self.assertEqual(seqof[i], value)
5503             if not seqof[i].ready:
5504                 seqof[i] = Integer(i)
5505         self.assertTrue(seqof.ready)
5506         repr(seqof)
5507         list(seqof.pps())
5508         pprint(seqof, big_blobs=True, with_decode_path=True)
5509
5510     def test_spec_mismatch(self):
5511         class SeqOf(self.base_klass):
5512             schema = Integer()
5513         seqof = SeqOf()
5514         seqof.append(Integer(123))
5515         with self.assertRaises(ValueError):
5516             seqof.append(Boolean(False))
5517         with self.assertRaises(ValueError):
5518             seqof[0] = Boolean(False)
5519
5520     @given(data_strategy())
5521     def test_bounds_satisfied(self, d):
5522         class SeqOf(self.base_klass):
5523             schema = Boolean()
5524         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
5525         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5526         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
5527         SeqOf(value=value, bounds=(bound_min, bound_max))
5528
5529     @given(data_strategy())
5530     def test_bounds_unsatisfied(self, d):
5531         class SeqOf(self.base_klass):
5532             schema = Boolean()
5533         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
5534         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5535         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
5536         with self.assertRaises(BoundsError) as err:
5537             SeqOf(value=value, bounds=(bound_min, bound_max))
5538         repr(err.exception)
5539         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5540             SeqOf(bounds=(bound_min, bound_max)).decode(
5541                 SeqOf(value).encode()
5542             )
5543         repr(err.exception)
5544         value = [Boolean(True)] * d.draw(integers(
5545             min_value=bound_max + 1,
5546             max_value=bound_max + 10,
5547         ))
5548         with self.assertRaises(BoundsError) as err:
5549             SeqOf(value=value, bounds=(bound_min, bound_max))
5550         repr(err.exception)
5551         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5552             SeqOf(bounds=(bound_min, bound_max)).decode(
5553                 SeqOf(value).encode()
5554             )
5555         repr(err.exception)
5556
5557     @given(integers(min_value=1, max_value=10))
5558     def test_out_of_bounds(self, bound_max):
5559         class SeqOf(self.base_klass):
5560             schema = Integer()
5561             bounds = (0, bound_max)
5562         seqof = SeqOf()
5563         for _ in range(bound_max):
5564             seqof.append(Integer(123))
5565         with self.assertRaises(BoundsError):
5566             seqof.append(Integer(123))
5567
5568     @given(data_strategy())
5569     def test_call(self, d):
5570         (
5571             schema_initial,
5572             value_initial,
5573             bounds_initial,
5574             impl_initial,
5575             expl_initial,
5576             default_initial,
5577             optional_initial,
5578             _decoded_initial,
5579         ) = d.draw(seqof_values_strategy())
5580
5581         class SeqOf(self.base_klass):
5582             schema = schema_initial
5583         obj_initial = SeqOf(
5584             value=value_initial,
5585             bounds=bounds_initial,
5586             impl=impl_initial,
5587             expl=expl_initial,
5588             default=default_initial,
5589             optional=optional_initial or False,
5590             _decoded=_decoded_initial,
5591         )
5592         (
5593             _,
5594             value,
5595             bounds,
5596             impl,
5597             expl,
5598             default,
5599             optional,
5600             _decoded,
5601         ) = d.draw(seqof_values_strategy(
5602             schema=schema_initial,
5603             do_expl=impl_initial is None,
5604         ))
5605         if (default is None) and (obj_initial.default is not None):
5606             bounds = None
5607         if (
5608                 (bounds is None) and
5609                 (value is not None) and
5610                 (bounds_initial is not None) and
5611                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
5612         ):
5613             value = None
5614         if (
5615                 (bounds is None) and
5616                 (default is not None) and
5617                 (bounds_initial is not None) and
5618                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
5619         ):
5620             default = None
5621         obj = obj_initial(
5622             value=value,
5623             bounds=bounds,
5624             impl=impl,
5625             expl=expl,
5626             default=default,
5627             optional=optional,
5628         )
5629         if obj.ready:
5630             value_expected = default if value is None else value
5631             value_expected = (
5632                 default_initial if value_expected is None
5633                 else value_expected
5634             )
5635             value_expected = () if value_expected is None else value_expected
5636             self.assertEqual(obj, value_expected)
5637         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5638         self.assertEqual(obj.expl_tag, expl or expl_initial)
5639         self.assertEqual(
5640             obj.default,
5641             default_initial if default is None else default,
5642         )
5643         if obj.default is None:
5644             optional = optional_initial if optional is None else optional
5645             optional = False if optional is None else optional
5646         else:
5647             optional = True
5648         self.assertEqual(obj.optional, optional)
5649         self.assertEqual(
5650             (obj._bound_min, obj._bound_max),
5651             bounds or bounds_initial or (0, float("+inf")),
5652         )
5653
5654     @given(seqof_values_strategy())
5655     def test_copy(self, values):
5656         _schema, value, bounds, impl, expl, default, optional, _decoded = values
5657
5658         class SeqOf(self.base_klass):
5659             schema = _schema
5660         obj = SeqOf(
5661             value=value,
5662             bounds=bounds,
5663             impl=impl,
5664             expl=expl,
5665             default=default,
5666             optional=optional or False,
5667             _decoded=_decoded,
5668         )
5669         obj_copied = obj.copy()
5670         self.assert_copied_basic_fields(obj, obj_copied)
5671         self.assertEqual(obj._bound_min, obj_copied._bound_min)
5672         self.assertEqual(obj._bound_max, obj_copied._bound_max)
5673         self.assertEqual(obj._value, obj_copied._value)
5674
5675     @given(
5676         lists(binary()),
5677         integers(min_value=1).map(tag_encode),
5678     )
5679     def test_stripped(self, values, tag_impl):
5680         class SeqOf(self.base_klass):
5681             schema = OctetString()
5682         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
5683         with self.assertRaises(NotEnoughData):
5684             obj.decode(obj.encode()[:-1])
5685
5686     @given(
5687         lists(binary()),
5688         integers(min_value=1).map(tag_ctxc),
5689     )
5690     def test_stripped_expl(self, values, tag_expl):
5691         class SeqOf(self.base_klass):
5692             schema = OctetString()
5693         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
5694         with self.assertRaises(NotEnoughData):
5695             obj.decode(obj.encode()[:-1])
5696
5697     @given(
5698         integers(min_value=31),
5699         integers(min_value=0),
5700         decode_path_strat,
5701     )
5702     def test_bad_tag(self, tag, offset, decode_path):
5703         with self.assertRaises(DecodeError) as err:
5704             self.base_klass().decode(
5705                 tag_encode(tag)[:-1],
5706                 offset=offset,
5707                 decode_path=decode_path,
5708             )
5709         repr(err.exception)
5710         self.assertEqual(err.exception.offset, offset)
5711         self.assertEqual(err.exception.decode_path, decode_path)
5712
5713     @given(
5714         integers(min_value=128),
5715         integers(min_value=0),
5716         decode_path_strat,
5717     )
5718     def test_bad_len(self, l, offset, decode_path):
5719         with self.assertRaises(DecodeError) as err:
5720             self.base_klass().decode(
5721                 self.base_klass.tag_default + len_encode(l)[:-1],
5722                 offset=offset,
5723                 decode_path=decode_path,
5724             )
5725         repr(err.exception)
5726         self.assertEqual(err.exception.offset, offset)
5727         self.assertEqual(err.exception.decode_path, decode_path)
5728
5729     @given(binary(min_size=1))
5730     def test_tag_mismatch(self, impl):
5731         assume(impl != self.base_klass.tag_default)
5732         with self.assertRaises(TagMismatch):
5733             self.base_klass(impl=impl).decode(self.base_klass().encode())
5734
5735     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5736     @given(
5737         seqof_values_strategy(schema=Integer()),
5738         lists(integers().map(Integer)),
5739         integers(min_value=1).map(tag_ctxc),
5740         integers(min_value=0),
5741         binary(max_size=5),
5742     )
5743     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
5744         _, _, _, _, _, default, optional, _decoded = values
5745
5746         class SeqOf(self.base_klass):
5747             schema = Integer()
5748         obj = SeqOf(
5749             value=value,
5750             default=default,
5751             optional=optional,
5752             _decoded=_decoded,
5753         )
5754         repr(obj)
5755         list(obj.pps())
5756         pprint(obj, big_blobs=True, with_decode_path=True)
5757         self.assertFalse(obj.expled)
5758         obj_encoded = obj.encode()
5759         obj_expled = obj(value, expl=tag_expl)
5760         self.assertTrue(obj_expled.expled)
5761         repr(obj_expled)
5762         list(obj_expled.pps())
5763         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5764         obj_expled_encoded = obj_expled.encode()
5765         ctx_copied = deepcopy(ctx_dummy)
5766         obj_decoded, tail = obj_expled.decode(
5767             obj_expled_encoded + tail_junk,
5768             offset=offset,
5769             ctx=ctx_copied,
5770         )
5771         self.assertDictEqual(ctx_copied, ctx_dummy)
5772         repr(obj_decoded)
5773         list(obj_decoded.pps())
5774         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5775         self.assertEqual(tail, tail_junk)
5776         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
5777         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5778         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5779         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5780         self.assertEqual(
5781             obj_decoded.expl_llen,
5782             len(len_encode(len(obj_encoded))),
5783         )
5784         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5785         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5786         self.assertEqual(
5787             obj_decoded.offset,
5788             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5789         )
5790         self.assertEqual(obj_decoded.expl_offset, offset)
5791         for obj_inner in obj_decoded:
5792             self.assertIn(obj_inner, obj_decoded)
5793             self.assertSequenceEqual(
5794                 obj_inner.encode(),
5795                 obj_expled_encoded[
5796                     obj_inner.offset - offset:
5797                     obj_inner.offset + obj_inner.tlvlen - offset
5798                 ],
5799             )
5800
5801         t, _, lv = tag_strip(obj_encoded)
5802         _, _, v = len_decode(lv)
5803         obj_encoded_lenindef = t + LENINDEF + v + EOC
5804         obj_decoded_lenindef, tail_lenindef = obj.decode(
5805             obj_encoded_lenindef + tail_junk,
5806             ctx={"bered": True},
5807         )
5808         self.assertTrue(obj_decoded_lenindef.lenindef)
5809         self.assertTrue(obj_decoded_lenindef.bered)
5810         obj_decoded_lenindef = obj_decoded_lenindef.copy()
5811         self.assertTrue(obj_decoded_lenindef.lenindef)
5812         self.assertTrue(obj_decoded_lenindef.bered)
5813         repr(obj_decoded_lenindef)
5814         list(obj_decoded_lenindef.pps())
5815         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
5816         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
5817         with self.assertRaises(DecodeError):
5818             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
5819         with self.assertRaises(DecodeError):
5820             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
5821
5822     @given(data_strategy())
5823     def test_bered(self, d):
5824         class SeqOf(self.base_klass):
5825             schema = Boolean()
5826         encoded = Boolean(False).encode()
5827         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
5828         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
5829         with self.assertRaises(DecodeError):
5830             SeqOf().decode(encoded)
5831         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
5832         self.assertFalse(decoded.ber_encoded)
5833         self.assertFalse(decoded.lenindef)
5834         self.assertTrue(decoded.bered)
5835         decoded = decoded.copy()
5836         self.assertFalse(decoded.ber_encoded)
5837         self.assertFalse(decoded.lenindef)
5838         self.assertTrue(decoded.bered)
5839
5840         class SeqOf(self.base_klass):
5841             schema = OctetString()
5842         encoded = OctetString(b"whatever").encode()
5843         encoded += (
5844             tag_encode(form=TagFormConstructed, num=4) +
5845             LENINDEF +
5846             OctetString(b"whatever").encode() +
5847             EOC
5848         )
5849         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
5850         with self.assertRaises(DecodeError):
5851             SeqOf().decode(encoded)
5852         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
5853         self.assertFalse(decoded.ber_encoded)
5854         self.assertFalse(decoded.lenindef)
5855         self.assertTrue(decoded.bered)
5856         decoded = decoded.copy()
5857         self.assertFalse(decoded.ber_encoded)
5858         self.assertFalse(decoded.lenindef)
5859         self.assertTrue(decoded.bered)
5860
5861
5862 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
5863     class SeqOf(SequenceOf):
5864         schema = "whatever"
5865     base_klass = SeqOf
5866
5867     def _test_symmetric_compare_objs(self, obj1, obj2):
5868         self.assertEqual(obj1, obj2)
5869         self.assertSequenceEqual(list(obj1), list(obj2))
5870
5871
5872 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
5873     class SeqOf(SetOf):
5874         schema = "whatever"
5875     base_klass = SeqOf
5876
5877     def _test_symmetric_compare_objs(self, obj1, obj2):
5878         self.assertSetEqual(
5879             set(int(v) for v in obj1),
5880             set(int(v) for v in obj2),
5881         )
5882
5883     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5884     @given(data_strategy())
5885     def test_sorted(self, d):
5886         values = [OctetString(v) for v in d.draw(lists(binary()))]
5887
5888         class Seq(SetOf):
5889             schema = OctetString()
5890         seq = Seq(values)
5891         seq_encoded = seq.encode()
5892         seq_decoded, _ = seq.decode(seq_encoded)
5893         self.assertSequenceEqual(
5894             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5895             b"".join(sorted([v.encode() for v in values])),
5896         )
5897
5898     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5899     @given(data_strategy())
5900     def test_unsorted(self, d):
5901         values = [OctetString(v).encode() for v in d.draw(sets(
5902             binary(min_size=1, max_size=5),
5903             min_size=2,
5904             max_size=5,
5905         ))]
5906         values = d.draw(permutations(values))
5907         assume(values != sorted(values))
5908         encoded = b"".join(values)
5909         seq_encoded = b"".join((
5910             SetOf.tag_default,
5911             len_encode(len(encoded)),
5912             encoded,
5913         ))
5914
5915         class Seq(SetOf):
5916             schema = OctetString()
5917         seq = Seq()
5918         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
5919             seq.decode(seq_encoded)
5920
5921         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
5922             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
5923             self.assertTrue(seq_decoded.ber_encoded)
5924             self.assertTrue(seq_decoded.bered)
5925             seq_decoded = seq_decoded.copy()
5926             self.assertTrue(seq_decoded.ber_encoded)
5927             self.assertTrue(seq_decoded.bered)
5928             self.assertSequenceEqual(
5929                 [obj.encode() for obj in seq_decoded],
5930                 values,
5931             )
5932
5933
5934 class TestGoMarshalVectors(TestCase):
5935     def runTest(self):
5936         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
5937         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
5938         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
5939         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
5940         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
5941
5942         class Seq(Sequence):
5943             schema = (
5944                 ("erste", Integer()),
5945                 ("zweite", Integer(optional=True))
5946             )
5947         seq = Seq()
5948         seq["erste"] = Integer(64)
5949         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5950         seq["erste"] = Integer(0x123456)
5951         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
5952         seq["erste"] = Integer(64)
5953         seq["zweite"] = Integer(65)
5954         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
5955
5956         class NestedSeq(Sequence):
5957             schema = (
5958                 ("nest", Seq()),
5959             )
5960         seq["erste"] = Integer(127)
5961         seq["zweite"] = None
5962         nested = NestedSeq()
5963         nested["nest"] = seq
5964         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
5965
5966         self.assertSequenceEqual(
5967             OctetString(b"\x01\x02\x03").encode(),
5968             hexdec("0403010203"),
5969         )
5970
5971         class Seq(Sequence):
5972             schema = (
5973                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
5974             )
5975         seq = Seq()
5976         seq["erste"] = Integer(64)
5977         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
5978
5979         class Seq(Sequence):
5980             schema = (
5981                 ("erste", Integer(expl=tag_ctxc(5))),
5982             )
5983         seq = Seq()
5984         seq["erste"] = Integer(64)
5985         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
5986
5987         class Seq(Sequence):
5988             schema = (
5989                 ("erste", Null(
5990                     impl=tag_encode(0, klass=TagClassContext),
5991                     optional=True,
5992                 )),
5993             )
5994         seq = Seq()
5995         seq["erste"] = Null()
5996         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
5997         seq["erste"] = None
5998         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5999
6000         self.assertSequenceEqual(
6001             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
6002             hexdec("170d3730303130313030303030305a"),
6003         )
6004         self.assertSequenceEqual(
6005             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
6006             hexdec("170d3039313131353232353631365a"),
6007         )
6008         self.assertSequenceEqual(
6009             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
6010             hexdec("180f32313030303430353132303130315a"),
6011         )
6012
6013         class Seq(Sequence):
6014             schema = (
6015                 ("erste", GeneralizedTime()),
6016             )
6017         seq = Seq()
6018         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
6019         self.assertSequenceEqual(
6020             seq.encode(),
6021             hexdec("3011180f32303039313131353232353631365a"),
6022         )
6023
6024         self.assertSequenceEqual(
6025             BitString((1, b"\x80")).encode(),
6026             hexdec("03020780"),
6027         )
6028         self.assertSequenceEqual(
6029             BitString((12, b"\x81\xF0")).encode(),
6030             hexdec("03030481f0"),
6031         )
6032
6033         self.assertSequenceEqual(
6034             ObjectIdentifier("1.2.3.4").encode(),
6035             hexdec("06032a0304"),
6036         )
6037         self.assertSequenceEqual(
6038             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
6039             hexdec("06092a864888932d010105"),
6040         )
6041         self.assertSequenceEqual(
6042             ObjectIdentifier("2.100.3").encode(),
6043             hexdec("0603813403"),
6044         )
6045
6046         self.assertSequenceEqual(
6047             PrintableString("test").encode(),
6048             hexdec("130474657374"),
6049         )
6050         self.assertSequenceEqual(
6051             PrintableString("x" * 127).encode(),
6052             hexdec("137F" + "78" * 127),
6053         )
6054         self.assertSequenceEqual(
6055             PrintableString("x" * 128).encode(),
6056             hexdec("138180" + "78" * 128),
6057         )
6058         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
6059
6060         class Seq(Sequence):
6061             schema = (
6062                 ("erste", IA5String()),
6063             )
6064         seq = Seq()
6065         seq["erste"] = IA5String("test")
6066         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
6067
6068         class Seq(Sequence):
6069             schema = (
6070                 ("erste", PrintableString()),
6071             )
6072         seq = Seq()
6073         seq["erste"] = PrintableString("test")
6074         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
6075         # Asterisk is actually not allowable
6076         PrintableString._allowable_chars |= set(b"*")
6077         seq["erste"] = PrintableString("test*")
6078         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
6079         PrintableString._allowable_chars -= set(b"*")
6080
6081         class Seq(Sequence):
6082             schema = (
6083                 ("erste", Any(optional=True)),
6084                 ("zweite", Integer()),
6085             )
6086         seq = Seq()
6087         seq["zweite"] = Integer(64)
6088         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6089
6090         class Seq(SetOf):
6091             schema = Integer()
6092         seq = Seq()
6093         seq.append(Integer(10))
6094         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
6095
6096         class _SeqOf(SequenceOf):
6097             schema = PrintableString()
6098
6099         class SeqOf(SequenceOf):
6100             schema = _SeqOf()
6101         _seqof = _SeqOf()
6102         _seqof.append(PrintableString("1"))
6103         seqof = SeqOf()
6104         seqof.append(_seqof)
6105         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
6106
6107         class Seq(Sequence):
6108             schema = (
6109                 ("erste", Integer(default=1)),
6110             )
6111         seq = Seq()
6112         seq["erste"] = Integer(0)
6113         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
6114         seq["erste"] = Integer(1)
6115         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6116         seq["erste"] = Integer(2)
6117         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
6118
6119
6120 class TestPP(TestCase):
6121     @given(data_strategy())
6122     def test_oid_printing(self, d):
6123         oids = {
6124             str(ObjectIdentifier(k)): v * 2
6125             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
6126         }
6127         chosen = d.draw(sampled_from(sorted(oids)))
6128         chosen_id = oids[chosen]
6129         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
6130         self.assertNotIn(chosen_id, pp_console_row(pp))
6131         self.assertIn(
6132             chosen_id,
6133             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
6134         )
6135
6136
6137 class TestAutoAddSlots(TestCase):
6138     def runTest(self):
6139         class Inher(Integer):
6140             pass
6141
6142         with self.assertRaises(AttributeError):
6143             inher = Inher()
6144             inher.unexistent = "whatever"
6145
6146
6147 class TestOIDDefines(TestCase):
6148     @given(data_strategy())
6149     def runTest(self, d):
6150         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
6151         value_name_chosen = d.draw(sampled_from(value_names))
6152         oids = [
6153             ObjectIdentifier(oid)
6154             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
6155         ]
6156         oid_chosen = d.draw(sampled_from(oids))
6157         values = d.draw(lists(
6158             integers(),
6159             min_size=len(value_names),
6160             max_size=len(value_names),
6161         ))
6162         _schema = [
6163             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
6164                 oid: Integer() for oid in oids[:-1]
6165             }),))),
6166         ]
6167         for i, value_name in enumerate(value_names):
6168             _schema.append((value_name, Any(expl=tag_ctxp(i))))
6169
6170         class Seq(Sequence):
6171             schema = _schema
6172         seq = Seq()
6173         for value_name, value in zip(value_names, values):
6174             seq[value_name] = Any(Integer(value).encode())
6175         seq["type"] = oid_chosen
6176         seq, _ = Seq().decode(seq.encode())
6177         for value_name in value_names:
6178             if value_name == value_name_chosen:
6179                 continue
6180             self.assertIsNone(seq[value_name].defined)
6181         if value_name_chosen in oids[:-1]:
6182             self.assertIsNotNone(seq[value_name_chosen].defined)
6183             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
6184             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
6185         repr(seq)
6186         list(seq.pps())
6187         pprint(seq, big_blobs=True, with_decode_path=True)
6188
6189
6190 class TestDefinesByPath(TestCase):
6191     def test_generated(self):
6192         class Seq(Sequence):
6193             schema = (
6194                 ("type", ObjectIdentifier()),
6195                 ("value", OctetString(expl=tag_ctxc(123))),
6196             )
6197
6198         class SeqInner(Sequence):
6199             schema = (
6200                 ("typeInner", ObjectIdentifier()),
6201                 ("valueInner", Any()),
6202             )
6203
6204         class PairValue(SetOf):
6205             schema = Any()
6206
6207         class Pair(Sequence):
6208             schema = (
6209                 ("type", ObjectIdentifier()),
6210                 ("value", PairValue()),
6211             )
6212
6213         class Pairs(SequenceOf):
6214             schema = Pair()
6215
6216         (
6217             type_integered,
6218             type_sequenced,
6219             type_innered,
6220             type_octet_stringed,
6221         ) = [
6222             ObjectIdentifier(oid)
6223             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
6224         ]
6225         seq_integered = Seq()
6226         seq_integered["type"] = type_integered
6227         seq_integered["value"] = OctetString(Integer(123).encode())
6228         seq_integered_raw = seq_integered.encode()
6229
6230         pairs = Pairs()
6231         pairs_input = (
6232             (type_octet_stringed, OctetString(b"whatever")),
6233             (type_integered, Integer(123)),
6234             (type_octet_stringed, OctetString(b"whenever")),
6235             (type_integered, Integer(234)),
6236         )
6237         for t, v in pairs_input:
6238             pair = Pair()
6239             pair["type"] = t
6240             pair["value"] = PairValue((Any(v),))
6241             pairs.append(pair)
6242         seq_inner = SeqInner()
6243         seq_inner["typeInner"] = type_innered
6244         seq_inner["valueInner"] = Any(pairs)
6245         seq_sequenced = Seq()
6246         seq_sequenced["type"] = type_sequenced
6247         seq_sequenced["value"] = OctetString(seq_inner.encode())
6248         seq_sequenced_raw = seq_sequenced.encode()
6249         repr(seq_sequenced)
6250         list(seq_sequenced.pps())
6251         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6252
6253         defines_by_path = []
6254         ctx_copied = deepcopy(ctx_dummy)
6255         seq_integered, _ = Seq().decode(
6256             seq_integered_raw,
6257             ctx=ctx_copied,
6258         )
6259         self.assertDictEqual(ctx_copied, ctx_dummy)
6260         self.assertIsNone(seq_integered["value"].defined)
6261         defines_by_path.append(
6262             (("type",), ((("value",), {
6263                 type_integered: Integer(),
6264                 type_sequenced: SeqInner(),
6265             }),))
6266         )
6267         ctx_copied["defines_by_path"] = defines_by_path
6268         seq_integered, _ = Seq().decode(
6269             seq_integered_raw,
6270             ctx=ctx_copied,
6271         )
6272         del ctx_copied["defines_by_path"]
6273         self.assertDictEqual(ctx_copied, ctx_dummy)
6274         self.assertIsNotNone(seq_integered["value"].defined)
6275         self.assertEqual(seq_integered["value"].defined[0], type_integered)
6276         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
6277         self.assertTrue(seq_integered_raw[
6278             seq_integered["value"].defined[1].offset:
6279         ].startswith(Integer(123).encode()))
6280         repr(seq_integered)
6281         list(seq_integered.pps())
6282         pprint(seq_integered, big_blobs=True, with_decode_path=True)
6283
6284         ctx_copied["defines_by_path"] = defines_by_path
6285         seq_sequenced, _ = Seq().decode(
6286             seq_sequenced_raw,
6287             ctx=ctx_copied,
6288         )
6289         del ctx_copied["defines_by_path"]
6290         self.assertDictEqual(ctx_copied, ctx_dummy)
6291         self.assertIsNotNone(seq_sequenced["value"].defined)
6292         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6293         seq_inner = seq_sequenced["value"].defined[1]
6294         self.assertIsNone(seq_inner["valueInner"].defined)
6295         repr(seq_sequenced)
6296         list(seq_sequenced.pps())
6297         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6298
6299         defines_by_path.append((
6300             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
6301             ((("valueInner",), {type_innered: Pairs()}),),
6302         ))
6303         ctx_copied["defines_by_path"] = defines_by_path
6304         seq_sequenced, _ = Seq().decode(
6305             seq_sequenced_raw,
6306             ctx=ctx_copied,
6307         )
6308         del ctx_copied["defines_by_path"]
6309         self.assertDictEqual(ctx_copied, ctx_dummy)
6310         self.assertIsNotNone(seq_sequenced["value"].defined)
6311         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6312         seq_inner = seq_sequenced["value"].defined[1]
6313         self.assertIsNotNone(seq_inner["valueInner"].defined)
6314         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
6315         pairs = seq_inner["valueInner"].defined[1]
6316         for pair in pairs:
6317             self.assertIsNone(pair["value"][0].defined)
6318         repr(seq_sequenced)
6319         list(seq_sequenced.pps())
6320         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6321
6322         defines_by_path.append((
6323             (
6324                 "value",
6325                 DecodePathDefBy(type_sequenced),
6326                 "valueInner",
6327                 DecodePathDefBy(type_innered),
6328                 any,
6329                 "type",
6330             ),
6331             ((("value",), {
6332                 type_integered: Integer(),
6333                 type_octet_stringed: OctetString(),
6334             }),),
6335         ))
6336         ctx_copied["defines_by_path"] = defines_by_path
6337         seq_sequenced, _ = Seq().decode(
6338             seq_sequenced_raw,
6339             ctx=ctx_copied,
6340         )
6341         del ctx_copied["defines_by_path"]
6342         self.assertDictEqual(ctx_copied, ctx_dummy)
6343         self.assertIsNotNone(seq_sequenced["value"].defined)
6344         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6345         seq_inner = seq_sequenced["value"].defined[1]
6346         self.assertIsNotNone(seq_inner["valueInner"].defined)
6347         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
6348         pairs_got = seq_inner["valueInner"].defined[1]
6349         for pair_input, pair_got in zip(pairs_input, pairs_got):
6350             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
6351             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
6352         repr(seq_sequenced)
6353         list(seq_sequenced.pps())
6354         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6355
6356     @given(oid_strategy(), integers())
6357     def test_simple(self, oid, tgt):
6358         class Inner(Sequence):
6359             schema = (
6360                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
6361                     ObjectIdentifier(oid): Integer(),
6362                 }),))),
6363             )
6364
6365         class Outer(Sequence):
6366             schema = (
6367                 ("inner", Inner()),
6368                 ("tgt", OctetString()),
6369             )
6370
6371         inner = Inner()
6372         inner["oid"] = ObjectIdentifier(oid)
6373         outer = Outer()
6374         outer["inner"] = inner
6375         outer["tgt"] = OctetString(Integer(tgt).encode())
6376         decoded, _ = Outer().decode(outer.encode())
6377         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
6378
6379
6380 class TestAbsDecodePath(TestCase):
6381     @given(
6382         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
6383         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
6384     )
6385     def test_concat(self, decode_path, rel_path):
6386         self.assertSequenceEqual(
6387             abs_decode_path(decode_path, rel_path),
6388             decode_path + rel_path,
6389         )
6390
6391     @given(
6392         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
6393         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
6394     )
6395     def test_abs(self, decode_path, rel_path):
6396         self.assertSequenceEqual(
6397             abs_decode_path(decode_path, ("/",) + rel_path),
6398             rel_path,
6399         )
6400
6401     @given(
6402         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
6403         integers(min_value=1, max_value=3),
6404         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
6405     )
6406     def test_dots(self, decode_path, number_of_dots, rel_path):
6407         self.assertSequenceEqual(
6408             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
6409             decode_path[:-number_of_dots] + rel_path,
6410         )
6411
6412
6413 class TestStrictDefaultExistence(TestCase):
6414     @given(data_strategy())
6415     def runTest(self, d):
6416         count = d.draw(integers(min_value=1, max_value=10))
6417         chosen = d.draw(integers(min_value=0, max_value=count - 1))
6418         _schema = [
6419             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
6420             for i in range(count)
6421         ]
6422         for klass in (Sequence, Set):
6423             class Seq(klass):
6424                 schema = _schema
6425             seq = Seq()
6426             for i in range(count):
6427                 seq["int%d" % i] = Integer(123)
6428             raw = seq.encode()
6429             chosen_choice = "int%d" % chosen
6430             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
6431             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6432                 seq.decode(raw)
6433             decoded, _ = seq.decode(raw, ctx={"allow_default_values": 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             decoded, _ = seq.decode(raw, ctx={"bered": True})
6440             self.assertTrue(decoded.ber_encoded)
6441             self.assertTrue(decoded.bered)
6442             decoded = decoded.copy()
6443             self.assertTrue(decoded.ber_encoded)
6444             self.assertTrue(decoded.bered)
6445
6446
6447 class TestX690PrefixedType(TestCase):
6448     def runTest(self):
6449         self.assertSequenceEqual(
6450             VisibleString("Jones").encode(),
6451             hexdec("1A054A6F6E6573"),
6452         )
6453         self.assertSequenceEqual(
6454             VisibleString(
6455                 "Jones",
6456                 impl=tag_encode(3, klass=TagClassApplication),
6457             ).encode(),
6458             hexdec("43054A6F6E6573"),
6459         )
6460         self.assertSequenceEqual(
6461             Any(
6462                 VisibleString(
6463                     "Jones",
6464                     impl=tag_encode(3, klass=TagClassApplication),
6465                 ),
6466                 expl=tag_ctxc(2),
6467             ).encode(),
6468             hexdec("A20743054A6F6E6573"),
6469         )
6470         self.assertSequenceEqual(
6471             OctetString(
6472                 VisibleString(
6473                     "Jones",
6474                     impl=tag_encode(3, klass=TagClassApplication),
6475                 ).encode(),
6476                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
6477             ).encode(),
6478             hexdec("670743054A6F6E6573"),
6479         )
6480         self.assertSequenceEqual(
6481             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
6482             hexdec("82054A6F6E6573"),
6483         )
6484
6485
6486 class TestExplOOB(TestCase):
6487     def runTest(self):
6488         expl = tag_ctxc(123)
6489         raw = Integer(123).encode() + Integer(234).encode()
6490         raw = b"".join((expl, len_encode(len(raw)), raw))
6491         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
6492             Integer(expl=expl).decode(raw)
6493         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})