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