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