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