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