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