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