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