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