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