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