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