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