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