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