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