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