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