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