]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
9a38fdda58bccb1837161a3237cbd5fdc9771c3d
[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         return "".join(six_unichr(c) for c in six_xrange(256))
3466
3467     @given(booleans())
3468     def test_optional(self, optional):
3469         obj = self.base_klass(default=self.base_klass(""), optional=optional)
3470         self.assertTrue(obj.optional)
3471
3472     @given(data_strategy())
3473     def test_ready(self, d):
3474         obj = self.base_klass()
3475         self.assertFalse(obj.ready)
3476         repr(obj)
3477         list(obj.pps())
3478         pprint(obj, big_blobs=True, with_decode_path=True)
3479         text_type(obj)
3480         with self.assertRaises(ObjNotReady) as err:
3481             obj.encode()
3482         repr(err.exception)
3483         with self.assertRaises(ObjNotReady) as err:
3484             encode2pass(obj)
3485         value = d.draw(text(alphabet=self.text_alphabet()))
3486         obj = self.base_klass(value)
3487         self.assertTrue(obj.ready)
3488         repr(obj)
3489         list(obj.pps())
3490         pprint(obj, big_blobs=True, with_decode_path=True)
3491         text_type(obj)
3492
3493     @given(data_strategy())
3494     def test_comparison(self, d):
3495         value1 = d.draw(text(alphabet=self.text_alphabet()))
3496         value2 = d.draw(text(alphabet=self.text_alphabet()))
3497         tag1 = d.draw(binary(min_size=1))
3498         tag2 = d.draw(binary(min_size=1))
3499         obj1 = self.base_klass(value1)
3500         obj2 = self.base_klass(value2)
3501         self.assertEqual(obj1 == obj2, value1 == value2)
3502         self.assertEqual(obj1 != obj2, value1 != value2)
3503         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3504         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
3505         obj1 = self.base_klass(value1, impl=tag1)
3506         obj2 = self.base_klass(value1, impl=tag2)
3507         self.assertEqual(obj1 == obj2, tag1 == tag2)
3508         self.assertEqual(obj1 != obj2, tag1 != tag2)
3509
3510     @given(data_strategy())
3511     def test_bounds_satisfied(self, d):
3512         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
3513         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3514         value = d.draw(text(
3515             alphabet=self.text_alphabet(),
3516             min_size=bound_min,
3517             max_size=bound_max,
3518         ))
3519         self.base_klass(value=value, bounds=(bound_min, bound_max))
3520
3521     @given(data_strategy())
3522     def test_bounds_unsatisfied(self, d):
3523         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
3524         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
3525         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
3526         with self.assertRaises(BoundsError) as err:
3527             self.base_klass(value=value, bounds=(bound_min, bound_max))
3528         repr(err.exception)
3529         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3530             self.base_klass(bounds=(bound_min, bound_max)).decode(
3531                 self.base_klass(value).encode()
3532             )
3533         repr(err.exception)
3534         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3535             self.base_klass(bounds=(bound_min, bound_max)).decode(
3536                 encode2pass(self.base_klass(value))
3537             )
3538         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
3539         with self.assertRaises(BoundsError) as err:
3540             self.base_klass(value=value, bounds=(bound_min, bound_max))
3541         repr(err.exception)
3542         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3543             self.base_klass(bounds=(bound_min, bound_max)).decode(
3544                 self.base_klass(value).encode()
3545             )
3546         repr(err.exception)
3547         with assertRaisesRegex(self, DecodeError, "bounds") as err:
3548             self.base_klass(bounds=(bound_min, bound_max)).decode(
3549                 encode2pass(self.base_klass(value))
3550             )
3551
3552     @given(data_strategy())
3553     def test_call(self, d):
3554         (
3555             value_initial,
3556             bounds_initial,
3557             impl_initial,
3558             expl_initial,
3559             default_initial,
3560             optional_initial,
3561             _decoded_initial,
3562         ) = d.draw(string_values_strategy(self.text_alphabet()))
3563         obj_initial = self.base_klass(
3564             value_initial,
3565             bounds_initial,
3566             impl_initial,
3567             expl_initial,
3568             default_initial,
3569             optional_initial or False,
3570             _decoded_initial,
3571         )
3572         (
3573             value,
3574             bounds,
3575             impl,
3576             expl,
3577             default,
3578             optional,
3579             _decoded,
3580         ) = d.draw(string_values_strategy(
3581             self.text_alphabet(),
3582             do_expl=impl_initial is None,
3583         ))
3584         if (default is None) and (obj_initial.default is not None):
3585             bounds = None
3586         if (
3587                 (bounds is None) and
3588                 (value is not None) and
3589                 (bounds_initial is not None) and
3590                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
3591         ):
3592             value = None
3593         if (
3594                 (bounds is None) and
3595                 (default is not None) and
3596                 (bounds_initial is not None) and
3597                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
3598         ):
3599             default = None
3600         obj = obj_initial(value, bounds, impl, expl, default, optional)
3601         if obj.ready:
3602             value_expected = default if value is None else value
3603             value_expected = (
3604                 default_initial if value_expected is None
3605                 else value_expected
3606             )
3607             self.assertEqual(obj, value_expected)
3608         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3609         self.assertEqual(obj.expl_tag, expl or expl_initial)
3610         self.assertEqual(
3611             obj.default,
3612             default_initial if default is None else default,
3613         )
3614         if obj.default is None:
3615             optional = optional_initial if optional is None else optional
3616             optional = False if optional is None else optional
3617         else:
3618             optional = True
3619         self.assertEqual(obj.optional, optional)
3620         self.assertEqual(
3621             (obj._bound_min, obj._bound_max),
3622             bounds or bounds_initial or (0, float("+inf")),
3623         )
3624
3625     @given(data_strategy())
3626     def test_copy(self, d):
3627         values = d.draw(string_values_strategy(self.text_alphabet()))
3628         obj = self.base_klass(*values)
3629         for copy_func in copy_funcs:
3630             obj_copied = copy_func(obj)
3631             self.assert_copied_basic_fields(obj, obj_copied)
3632             self.assertEqual(obj._bound_min, obj_copied._bound_min)
3633             self.assertEqual(obj._bound_max, obj_copied._bound_max)
3634             self.assertEqual(obj._value, obj_copied._value)
3635
3636     @given(data_strategy())
3637     def test_stripped(self, d):
3638         value = d.draw(text(alphabet=self.text_alphabet()))
3639         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3640         obj = self.base_klass(value, impl=tag_impl)
3641         with self.assertRaises(NotEnoughData):
3642             obj.decode(obj.encode()[:-1])
3643
3644     @given(data_strategy())
3645     def test_stripped_expl(self, d):
3646         value = d.draw(text(alphabet=self.text_alphabet()))
3647         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3648         obj = self.base_klass(value, expl=tag_expl)
3649         with self.assertRaises(NotEnoughData):
3650             obj.decode(obj.encode()[:-1])
3651
3652     @given(
3653         integers(min_value=31),
3654         integers(min_value=0),
3655         decode_path_strat,
3656     )
3657     def test_bad_tag(self, tag, offset, decode_path):
3658         with self.assertRaises(DecodeError) as err:
3659             self.base_klass().decode(
3660                 tag_encode(tag)[:-1],
3661                 offset=offset,
3662                 decode_path=decode_path,
3663             )
3664         repr(err.exception)
3665         self.assertEqual(err.exception.offset, offset)
3666         self.assertEqual(err.exception.decode_path, decode_path)
3667
3668     @given(
3669         integers(min_value=128),
3670         integers(min_value=0),
3671         decode_path_strat,
3672     )
3673     def test_bad_len(self, l, offset, decode_path):
3674         with self.assertRaises(DecodeError) as err:
3675             self.base_klass().decode(
3676                 self.base_klass.tag_default + len_encode(l)[:-1],
3677                 offset=offset,
3678                 decode_path=decode_path,
3679             )
3680         repr(err.exception)
3681         self.assertEqual(err.exception.offset, offset)
3682         self.assertEqual(err.exception.decode_path, decode_path)
3683
3684     @given(
3685         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3686         integers(min_value=0),
3687         decode_path_strat,
3688     )
3689     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3690         value, bound_min = list(sorted(ints))
3691
3692         class String(self.base_klass):
3693             # Multiply this value by four, to satisfy UTF-32 bounds
3694             # (4 bytes per character) validation
3695             bounds = (bound_min * 4, bound_min * 4)
3696         with self.assertRaises(DecodeError) as err:
3697             String().decode(
3698                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
3699                 offset=offset,
3700                 decode_path=decode_path,
3701             )
3702         repr(err.exception)
3703         self.assertEqual(err.exception.offset, offset)
3704         self.assertEqual(err.exception.decode_path, decode_path)
3705
3706     @given(data_strategy())
3707     def test_symmetric(self, d):
3708         values = d.draw(string_values_strategy(self.text_alphabet()))
3709         value = d.draw(text(alphabet=self.text_alphabet()))
3710         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3711         offset = d.draw(integers(min_value=0))
3712         tail_junk = d.draw(binary(max_size=5))
3713         decode_path = d.draw(decode_path_strat)
3714         _, _, _, _, default, optional, _decoded = values
3715         obj = self.base_klass(
3716             value=value,
3717             default=default,
3718             optional=optional,
3719             _decoded=_decoded,
3720         )
3721         repr(obj)
3722         list(obj.pps())
3723         pprint(obj, big_blobs=True, with_decode_path=True)
3724         self.assertFalse(obj.expled)
3725         obj_encoded = obj.encode()
3726         self.assertEqual(encode2pass(obj), obj_encoded)
3727         obj_expled = obj(value, expl=tag_expl)
3728         self.assertTrue(obj_expled.expled)
3729         repr(obj_expled)
3730         list(obj_expled.pps())
3731         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3732         obj_expled_encoded = obj_expled.encode()
3733         ctx_copied = deepcopy(ctx_dummy)
3734         obj_decoded, tail = obj_expled.decode(
3735             obj_expled_encoded + tail_junk,
3736             offset=offset,
3737             ctx=ctx_copied,
3738         )
3739         self.assertDictEqual(ctx_copied, ctx_dummy)
3740         repr(obj_decoded)
3741         list(obj_decoded.pps())
3742         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3743         self.assertEqual(tail, tail_junk)
3744         self.assertEqual(obj_decoded, obj_expled)
3745         self.assertNotEqual(obj_decoded, obj)
3746         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3747         self.assertEqual(bytes(obj_decoded), bytes(obj))
3748         self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
3749         self.assertEqual(text_type(obj_decoded), text_type(obj))
3750         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3751         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3752         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3753         self.assertEqual(
3754             obj_decoded.expl_llen,
3755             len(len_encode(len(obj_encoded))),
3756         )
3757         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3758         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3759         self.assertEqual(
3760             obj_decoded.offset,
3761             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3762         )
3763         self.assertEqual(obj_decoded.expl_offset, offset)
3764         assert_exceeding_data(
3765             self,
3766             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3767             tail_junk,
3768         )
3769
3770         evgens = list(obj_expled.decode_evgen(
3771             obj_expled_encoded + tail_junk,
3772             offset=offset,
3773             decode_path=decode_path,
3774             ctx=ctx_copied,
3775         ))
3776         self.assertEqual(len(evgens), 1)
3777         _decode_path, obj, tail = evgens[0]
3778         self.assertSequenceEqual(tail, tail_junk)
3779         self.assertEqual(_decode_path, decode_path)
3780         if not getattr(self, "evgen_mode_skip_value", True):
3781             self.assertEqual(obj, obj_decoded)
3782         self.assertEqual(obj.expl_offset, offset)
3783         repr(obj)
3784         list(obj.pps())
3785
3786
3787 cyrillic_letters = text(
3788     alphabet="".join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
3789     min_size=1,
3790     max_size=5,
3791 )
3792
3793
3794 class TestUTF8String(StringMixin, CommonMixin, TestCase):
3795     base_klass = UTF8String
3796
3797     @given(cyrillic_letters)
3798     def test_byte_per_primitive(self, chars):
3799         char = chars[0]
3800         char_raw = char.encode("utf-8")
3801         encoded = b"".join((
3802             self.base_klass().tag_constructed,
3803             LENINDEF,
3804             OctetString(char_raw[:1]).encode(),
3805             OctetString(char_raw[1:2]).encode(),
3806             EOC,
3807         ))
3808         self.assertEqual(
3809             self.base_klass().decod(encoded, ctx={"bered": True}),
3810             char,
3811         )
3812
3813
3814 class UnicodeDecodeErrorMixin(object):
3815     @given(cyrillic_letters)
3816     def test_unicode_decode_error(self, cyrillic_text):
3817         with self.assertRaises(DecodeError):
3818             self.base_klass(cyrillic_text)
3819
3820
3821 class TestNumericString(StringMixin, CommonMixin, TestCase):
3822     base_klass = NumericString
3823
3824     def text_alphabet(self):
3825         return digits + " "
3826
3827     @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
3828     def test_non_numeric(self, non_numeric_text):
3829         with assertRaisesRegex(self, DecodeError, "alphabet value"):
3830             self.base_klass(non_numeric_text)
3831
3832     @given(
3833         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3834         integers(min_value=0),
3835         decode_path_strat,
3836     )
3837     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3838         value, bound_min = list(sorted(ints))
3839
3840         class String(self.base_klass):
3841             bounds = (bound_min, bound_min)
3842         with self.assertRaises(DecodeError) as err:
3843             String().decode(
3844                 self.base_klass(b"1" * value).encode(),
3845                 offset=offset,
3846                 decode_path=decode_path,
3847             )
3848         repr(err.exception)
3849         self.assertEqual(err.exception.offset, offset)
3850         self.assertEqual(err.exception.decode_path, decode_path)
3851
3852     def test_byte_per_primitive(self):
3853         encoded = b"".join((
3854             self.base_klass().tag_constructed,
3855             LENINDEF,
3856             OctetString(b"1").encode(),
3857             OctetString(b"2").encode(),
3858             EOC,
3859         ))
3860         self.assertEqual(
3861             self.base_klass().decod(encoded, ctx={"bered": True}),
3862             "12",
3863         )
3864
3865
3866 class TestPrintableString(
3867         UnicodeDecodeErrorMixin,
3868         StringMixin,
3869         CommonMixin,
3870         TestCase,
3871 ):
3872     base_klass = PrintableString
3873
3874     def text_alphabet(self):
3875         return ascii_letters + digits + " '()+,-./:=?"
3876
3877     @given(text(alphabet=sorted(set(whitespace) - set(" ")), min_size=1, max_size=5))
3878     def test_non_printable(self, non_printable_text):
3879         with assertRaisesRegex(self, DecodeError, "alphabet value"):
3880             self.base_klass(non_printable_text)
3881
3882     @given(
3883         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
3884         integers(min_value=0),
3885         decode_path_strat,
3886     )
3887     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
3888         value, bound_min = list(sorted(ints))
3889
3890         class String(self.base_klass):
3891             bounds = (bound_min, bound_min)
3892         with self.assertRaises(DecodeError) as err:
3893             String().decode(
3894                 self.base_klass(b"1" * value).encode(),
3895                 offset=offset,
3896                 decode_path=decode_path,
3897             )
3898         repr(err.exception)
3899         self.assertEqual(err.exception.offset, offset)
3900         self.assertEqual(err.exception.decode_path, decode_path)
3901
3902     def test_allowable_invalid_chars(self):
3903         for c, kwargs in (
3904                 ("*", {"allow_asterisk": True}),
3905                 ("&", {"allow_ampersand": True}),
3906                 ("&*", {"allow_asterisk": True, "allow_ampersand": True}),
3907         ):
3908             s = "hello invalid"
3909             obj = self.base_klass(s)
3910             for prop in kwargs.keys():
3911                 self.assertFalse(getattr(obj, prop))
3912             s += c
3913             with assertRaisesRegex(self, DecodeError, "alphabet value"):
3914                 self.base_klass(s)
3915             self.base_klass(s, **kwargs)
3916             klass = self.base_klass(**kwargs)
3917             obj = klass(s)
3918             for prop in kwargs.keys():
3919                 self.assertTrue(getattr(obj, prop))
3920             obj = copy(obj)
3921             obj(s)
3922             for prop in kwargs.keys():
3923                 self.assertTrue(getattr(obj, prop))
3924
3925
3926 class TestTeletexString(
3927         UnicodeDecodeErrorMixin,
3928         StringMixin,
3929         CommonMixin,
3930         TestCase,
3931 ):
3932     base_klass = TeletexString
3933
3934
3935 class TestVideotexString(
3936         UnicodeDecodeErrorMixin,
3937         StringMixin,
3938         CommonMixin,
3939         TestCase,
3940 ):
3941     base_klass = VideotexString
3942
3943
3944 class TestIA5String(
3945         UnicodeDecodeErrorMixin,
3946         StringMixin,
3947         CommonMixin,
3948         TestCase,
3949 ):
3950     base_klass = IA5String
3951
3952     def text_alphabet(self):
3953         return "".join(six_unichr(c) for c in six_xrange(128))
3954
3955     @given(integers(min_value=128, max_value=255))
3956     def test_alphabet_bad(self, code):
3957         with self.assertRaises(DecodeError):
3958             self.base_klass().decod(
3959                 self.base_klass.tag_default +
3960                 len_encode(1) +
3961                 bytes(bytearray([code])),
3962             )
3963
3964
3965 class TestGraphicString(
3966         UnicodeDecodeErrorMixin,
3967         StringMixin,
3968         CommonMixin,
3969         TestCase,
3970 ):
3971     base_klass = GraphicString
3972
3973
3974 class TestVisibleString(
3975         UnicodeDecodeErrorMixin,
3976         StringMixin,
3977         CommonMixin,
3978         TestCase,
3979 ):
3980     base_klass = VisibleString
3981
3982     def text_alphabet(self):
3983         return " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
3984
3985     def test_x690_vector(self):
3986         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3987         self.assertSequenceEqual(tail, b"")
3988         self.assertEqual(str(obj), "Jones")
3989         self.assertFalse(obj.ber_encoded)
3990         self.assertFalse(obj.lenindef)
3991         self.assertFalse(obj.bered)
3992
3993         obj, tail = VisibleString().decode(
3994             hexdec("3A0904034A6F6E04026573"),
3995             ctx={"bered": True},
3996         )
3997         self.assertSequenceEqual(tail, b"")
3998         self.assertEqual(str(obj), "Jones")
3999         self.assertTrue(obj.ber_encoded)
4000         self.assertFalse(obj.lenindef)
4001         self.assertTrue(obj.bered)
4002         obj = copy(obj)
4003         self.assertTrue(obj.ber_encoded)
4004         self.assertFalse(obj.lenindef)
4005         self.assertTrue(obj.bered)
4006
4007         obj, tail = VisibleString().decode(
4008             hexdec("3A8004034A6F6E040265730000"),
4009             ctx={"bered": True},
4010         )
4011         self.assertSequenceEqual(tail, b"")
4012         self.assertEqual(str(obj), "Jones")
4013         self.assertTrue(obj.ber_encoded)
4014         self.assertTrue(obj.lenindef)
4015         self.assertTrue(obj.bered)
4016         obj = copy(obj)
4017         self.assertTrue(obj.ber_encoded)
4018         self.assertTrue(obj.lenindef)
4019         self.assertTrue(obj.bered)
4020
4021     @given(one_of((
4022         integers(min_value=0, max_value=ord(" ") - 1),
4023         integers(min_value=ord("~") + 1, max_value=255),
4024     )))
4025     def test_alphabet_bad(self, code):
4026         with self.assertRaises(DecodeError):
4027             self.base_klass().decod(
4028                 self.base_klass.tag_default +
4029                 len_encode(1) +
4030                 bytes(bytearray([code])),
4031             )
4032
4033     @given(
4034         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
4035         integers(min_value=0),
4036         decode_path_strat,
4037     )
4038     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
4039         value, bound_min = list(sorted(ints))
4040
4041         class String(self.base_klass):
4042             bounds = (bound_min, bound_min)
4043         with self.assertRaises(DecodeError) as err:
4044             String().decode(
4045                 self.base_klass(b"1" * value).encode(),
4046                 offset=offset,
4047                 decode_path=decode_path,
4048             )
4049         repr(err.exception)
4050         self.assertEqual(err.exception.offset, offset)
4051         self.assertEqual(err.exception.decode_path, decode_path)
4052
4053
4054 class TestGeneralString(
4055         UnicodeDecodeErrorMixin,
4056         StringMixin,
4057         CommonMixin,
4058         TestCase,
4059 ):
4060     base_klass = GeneralString
4061
4062
4063 class TestUniversalString(StringMixin, CommonMixin, TestCase):
4064     base_klass = UniversalString
4065
4066
4067 class TestBMPString(StringMixin, CommonMixin, TestCase):
4068     base_klass = BMPString
4069
4070
4071 @composite
4072 def generalized_time_values_strategy(
4073         draw,
4074         min_datetime,
4075         max_datetime,
4076         omit_ms=False,
4077         do_expl=False,
4078 ):
4079     value = None
4080     if draw(booleans()):
4081         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
4082         if omit_ms:
4083             value = value.replace(microsecond=0)
4084     default = None
4085     if draw(booleans()):
4086         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
4087         if omit_ms:
4088             default = default.replace(microsecond=0)
4089     impl = None
4090     expl = None
4091     if do_expl:
4092         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4093     else:
4094         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4095     optional = draw(one_of(none(), booleans()))
4096     _decoded = (
4097         draw(integers(min_value=0)),
4098         draw(integers(min_value=0)),
4099         draw(integers(min_value=0)),
4100     )
4101     return (value, impl, expl, default, optional, _decoded)
4102
4103
4104 class TimeMixin(object):
4105     def test_invalid_value_type(self):
4106         with self.assertRaises(InvalidValueType) as err:
4107             self.base_klass(datetime.now().timetuple())
4108         repr(err.exception)
4109
4110     @given(data_strategy())
4111     def test_optional(self, d):
4112         default = d.draw(datetimes(
4113             min_value=self.min_datetime,
4114             max_value=self.max_datetime,
4115         ))
4116         optional = d.draw(booleans())
4117         obj = self.base_klass(default=default, optional=optional)
4118         self.assertTrue(obj.optional)
4119
4120     @given(data_strategy())
4121     def test_ready(self, d):
4122         obj = self.base_klass()
4123         self.assertFalse(obj.ready)
4124         repr(obj)
4125         list(obj.pps())
4126         pprint(obj, big_blobs=True, with_decode_path=True)
4127         with self.assertRaises(ObjNotReady) as err:
4128             obj.encode()
4129         repr(err.exception)
4130         with self.assertRaises(ObjNotReady) as err:
4131             encode2pass(obj)
4132         value = d.draw(datetimes(
4133             min_value=self.min_datetime,
4134             max_value=self.max_datetime,
4135         ))
4136         obj = self.base_klass(value)
4137         self.assertTrue(obj.ready)
4138         repr(obj)
4139         list(obj.pps())
4140         pprint(obj, big_blobs=True, with_decode_path=True)
4141
4142     @given(data_strategy())
4143     def test_comparison(self, d):
4144         value1 = d.draw(datetimes(
4145             min_value=self.min_datetime,
4146             max_value=self.max_datetime,
4147         ))
4148         value2 = d.draw(datetimes(
4149             min_value=self.min_datetime,
4150             max_value=self.max_datetime,
4151         ))
4152         tag1 = d.draw(binary(min_size=1))
4153         tag2 = d.draw(binary(min_size=1))
4154         if self.omit_ms:
4155             value1 = value1.replace(microsecond=0)
4156             value2 = value2.replace(microsecond=0)
4157         obj1 = self.base_klass(value1)
4158         obj2 = self.base_klass(value2)
4159         self.assertEqual(obj1 == obj2, value1 == value2)
4160         self.assertEqual(obj1 != obj2, value1 != value2)
4161         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
4162         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
4163         obj1 = self.base_klass(value1, impl=tag1)
4164         obj2 = self.base_klass(value1, impl=tag2)
4165         self.assertEqual(obj1 == obj2, tag1 == tag2)
4166         self.assertEqual(obj1 != obj2, tag1 != tag2)
4167
4168     @given(data_strategy())
4169     def test_call(self, d):
4170         (
4171             value_initial,
4172             impl_initial,
4173             expl_initial,
4174             default_initial,
4175             optional_initial,
4176             _decoded_initial,
4177         ) = d.draw(generalized_time_values_strategy(
4178             min_datetime=self.min_datetime,
4179             max_datetime=self.max_datetime,
4180             omit_ms=self.omit_ms,
4181         ))
4182         obj_initial = self.base_klass(
4183             value=value_initial,
4184             impl=impl_initial,
4185             expl=expl_initial,
4186             default=default_initial,
4187             optional=optional_initial or False,
4188             _decoded=_decoded_initial,
4189         )
4190         (
4191             value,
4192             impl,
4193             expl,
4194             default,
4195             optional,
4196             _decoded,
4197         ) = d.draw(generalized_time_values_strategy(
4198             min_datetime=self.min_datetime,
4199             max_datetime=self.max_datetime,
4200             omit_ms=self.omit_ms,
4201             do_expl=impl_initial is None,
4202         ))
4203         obj = obj_initial(
4204             value=value,
4205             impl=impl,
4206             expl=expl,
4207             default=default,
4208             optional=optional,
4209         )
4210         if obj.ready:
4211             value_expected = default if value is None else value
4212             value_expected = (
4213                 default_initial if value_expected is None
4214                 else value_expected
4215             )
4216             self.assertEqual(obj, value_expected)
4217         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4218         self.assertEqual(obj.expl_tag, expl or expl_initial)
4219         self.assertEqual(
4220             obj.default,
4221             default_initial if default is None else default,
4222         )
4223         if obj.default is None:
4224             optional = optional_initial if optional is None else optional
4225             optional = False if optional is None else optional
4226         else:
4227             optional = True
4228         self.assertEqual(obj.optional, optional)
4229
4230     @given(data_strategy())
4231     def test_copy(self, d):
4232         values = d.draw(generalized_time_values_strategy(
4233             min_datetime=self.min_datetime,
4234             max_datetime=self.max_datetime,
4235         ))
4236         obj = self.base_klass(*values)
4237         for copy_func in copy_funcs:
4238             obj_copied = copy_func(obj)
4239             self.assert_copied_basic_fields(obj, obj_copied)
4240             self.assertEqual(obj._value, obj_copied._value)
4241
4242     @given(data_strategy())
4243     def test_stripped(self, d):
4244         value = d.draw(datetimes(
4245             min_value=self.min_datetime,
4246             max_value=self.max_datetime,
4247         ))
4248         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4249         obj = self.base_klass(value, impl=tag_impl)
4250         with self.assertRaises(NotEnoughData):
4251             obj.decode(obj.encode()[:-1])
4252
4253     @given(data_strategy())
4254     def test_stripped_expl(self, d):
4255         value = d.draw(datetimes(
4256             min_value=self.min_datetime,
4257             max_value=self.max_datetime,
4258         ))
4259         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4260         obj = self.base_klass(value, expl=tag_expl)
4261         with self.assertRaises(NotEnoughData):
4262             obj.decode(obj.encode()[:-1])
4263
4264     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4265     @given(data_strategy())
4266     def test_symmetric(self, d):
4267         values = d.draw(generalized_time_values_strategy(
4268             min_datetime=self.min_datetime,
4269             max_datetime=self.max_datetime,
4270         ))
4271         value = d.draw(datetimes(
4272             min_value=self.min_datetime,
4273             max_value=self.max_datetime,
4274         ))
4275         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4276         offset = d.draw(integers(min_value=0))
4277         tail_junk = d.draw(binary(max_size=5))
4278         _, _, _, default, optional, _decoded = values
4279         obj = self.base_klass(
4280             value=value,
4281             default=default,
4282             optional=optional,
4283             _decoded=_decoded,
4284         )
4285         repr(obj)
4286         list(obj.pps())
4287         pprint(obj, big_blobs=True, with_decode_path=True)
4288         self.assertFalse(obj.expled)
4289         obj_encoded = obj.encode()
4290         self.assertEqual(encode2pass(obj), obj_encoded)
4291         self.additional_symmetric_check(value, obj_encoded)
4292         obj_expled = obj(value, expl=tag_expl)
4293         self.assertTrue(obj_expled.expled)
4294         repr(obj_expled)
4295         list(obj_expled.pps())
4296         pprint(obj_expled, big_blobs=True, with_decode_path=True)
4297         obj_expled_encoded = obj_expled.encode()
4298         ctx_copied = deepcopy(ctx_dummy)
4299         obj_decoded, tail = obj_expled.decode(
4300             obj_expled_encoded + tail_junk,
4301             offset=offset,
4302             ctx=ctx_copied,
4303         )
4304         self.assertDictEqual(ctx_copied, ctx_dummy)
4305         repr(obj_decoded)
4306         list(obj_decoded.pps())
4307         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4308         self.assertEqual(tail, tail_junk)
4309         self.assertEqual(obj_decoded, obj_expled)
4310         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
4311         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
4312         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4313         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4314         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4315         self.assertEqual(
4316             obj_decoded.expl_llen,
4317             len(len_encode(len(obj_encoded))),
4318         )
4319         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4320         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4321         self.assertEqual(
4322             obj_decoded.offset,
4323             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4324         )
4325         self.assertEqual(obj_decoded.expl_offset, offset)
4326         assert_exceeding_data(
4327             self,
4328             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
4329             tail_junk,
4330         )
4331
4332
4333 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
4334     base_klass = GeneralizedTime
4335     omit_ms = False
4336     min_datetime = datetime(1900, 1, 1)
4337     max_datetime = datetime(9999, 12, 31)
4338     evgen_mode_skip_value = False
4339
4340     def additional_symmetric_check(self, value, obj_encoded):
4341         if value.microsecond > 0:
4342             self.assertFalse(obj_encoded.endswith(b"0Z"))
4343
4344     def test_repr_not_ready(self):
4345         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
4346         repr(GeneralizedTime())
4347
4348     def test_x690_vector_valid(self):
4349         for data in ((
4350                 b"19920521000000Z",
4351                 b"19920622123421Z",
4352                 b"19920722132100.3Z",
4353         )):
4354             GeneralizedTime(data)
4355
4356     def test_x690_vector_invalid(self):
4357         for data in ((
4358                 b"19920520240000Z",
4359                 b"19920622123421.0Z",
4360                 b"19920722132100.30Z",
4361         )):
4362             with self.assertRaises(DecodeError) as err:
4363                 GeneralizedTime(data)
4364             repr(err.exception)
4365
4366     def test_go_vectors_invalid(self):
4367         for data in ((
4368                 b"20100102030405",
4369                 b"00000100000000Z",
4370                 b"20101302030405Z",
4371                 b"20100002030405Z",
4372                 b"20100100030405Z",
4373                 b"20100132030405Z",
4374                 b"20100231030405Z",
4375                 b"20100102240405Z",
4376                 b"20100102036005Z",
4377                 b"20100102030460Z",
4378                 b"-20100102030410Z",
4379                 b"2010-0102030410Z",
4380                 b"2010-0002030410Z",
4381                 b"201001-02030410Z",
4382                 b"20100102-030410Z",
4383                 b"2010010203-0410Z",
4384                 b"201001020304-10Z",
4385                 # These ones are INVALID in *DER*, but accepted
4386                 # by Go's encoding/asn1
4387                 b"20100102030405+0607",
4388                 b"20100102030405-0607",
4389         )):
4390             with self.assertRaises(DecodeError) as err:
4391                 GeneralizedTime(data)
4392             repr(err.exception)
4393
4394     def test_go_vectors_valid(self):
4395         self.assertEqual(
4396             GeneralizedTime(b"20100102030405Z").todatetime(),
4397             datetime(2010, 1, 2, 3, 4, 5, 0),
4398         )
4399
4400     def test_go_vectors_valid_ber(self):
4401         for data in ((
4402                 b"20100102030405+0607",
4403                 b"20100102030405-0607",
4404         )):
4405             GeneralizedTime(data, ctx={"bered": True})
4406
4407     def test_utc_offsets(self):
4408         """Some know equal UTC offsets
4409         """
4410         dts = [
4411             GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4412             for data in (
4413                 "200101011830Z",
4414                 "200101012230+04",
4415                 "200101011130-0700",
4416                 "200101011500-03:30",
4417             )
4418         ]
4419         self.assertEqual(dts[0], dts[1])
4420         self.assertEqual(dts[0], dts[2])
4421         self.assertEqual(dts[0], dts[3])
4422
4423     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4424     @given(data_strategy())
4425     def test_valid_ber(self, d):
4426         min_year = 1901 if PY2 else 2
4427         year = d.draw(integers(min_value=min_year, max_value=9999))
4428         month = d.draw(integers(min_value=1, max_value=12))
4429         day = d.draw(integers(min_value=1, max_value=28))
4430         hours = d.draw(integers(min_value=0, max_value=23))
4431         data = "%04d%02d%02d%02d" % (year, month, day, hours)
4432         dt = datetime(year, month, day, hours)
4433         fractions_sign = d.draw(sampled_from("  ,."))
4434         fractions = None
4435         if fractions_sign != " ":
4436             fractions = random()
4437         if d.draw(booleans()):
4438             minutes = d.draw(integers(min_value=0, max_value=59))
4439             data += "%02d" % minutes
4440             dt += timedelta(seconds=60 * minutes)
4441             if d.draw(booleans()):
4442                 seconds = d.draw(integers(min_value=0, max_value=59))
4443                 data += "%02d" % seconds
4444                 dt += timedelta(seconds=seconds)
4445                 if fractions is not None:
4446                     dt += timedelta(microseconds=10**6 * fractions)
4447             elif fractions is not None:
4448                 dt += timedelta(seconds=60 * fractions)
4449         elif fractions is not None:
4450             dt += timedelta(seconds=3600 * fractions)
4451         if fractions is not None:
4452             data += fractions_sign + str(fractions)[2:]
4453         if d.draw(booleans()):
4454             data += "Z"
4455         elif d.draw(booleans()):
4456             offset_hour = d.draw(integers(min_value=0, max_value=13))
4457             sign = 1
4458             if d.draw(booleans()):
4459                 data += "-"
4460                 sign = -1
4461             else:
4462                 data += "+"
4463             dt -= timedelta(seconds=sign * 3600 * offset_hour)
4464             data += "%02d" % offset_hour
4465             minutes_separator = d.draw(sampled_from((None, "", ":")))
4466             if minutes_separator is not None:
4467                 offset_minute = d.draw(integers(min_value=0, max_value=59))
4468                 dt -= timedelta(seconds=sign * 60 * offset_minute)
4469                 data += "%s%02d" % (minutes_separator, offset_minute)
4470         data = data.encode("ascii")
4471         data_der = GeneralizedTime.tag_default + len_encode(len(data)) + data
4472         try:
4473             GeneralizedTime().decod(data_der)
4474         except DecodeError:
4475             dered = False
4476         else:
4477             dered = True
4478         obj = GeneralizedTime().decod(data_der, ctx={"bered": True})
4479         if dt.year > 1970:
4480             self.assertEqual(
4481                 mktime(obj.todatetime().timetuple()),
4482                 mktime(dt.timetuple()),
4483             )
4484         elif not PY2:
4485             self.assertEqual(obj.todatetime().timestamp(), dt.timestamp())
4486         self.assertEqual(obj.ber_encoded, not dered)
4487         self.assertEqual(obj.bered, not dered)
4488         self.assertEqual(obj.ber_raw, None if dered else data)
4489         self.assertEqual(obj.encode() == data_der, dered)
4490         repr(obj)
4491         bytes(obj)
4492         str(obj)
4493
4494     def test_invalid_ber(self):
4495         for data in ((
4496                 # "00010203040506.07",
4497                 "-0010203040506.07",
4498                 "0001-203040506.07",
4499                 "000102-3040506.07",
4500                 "00010203-40506.07",
4501                 "0001020304-506.07",
4502                 "000102030405-6.07",
4503                 "00010203040506.-7",
4504                 "+0010203040506.07",
4505                 "0001+203040506.07",
4506                 "000102+3040506.07",
4507                 "00010203+40506.07",
4508                 "0001020304+506.07",
4509                 "000102030405+6.07",
4510                 "00010203040506.+7",
4511                 " 0010203040506.07",
4512                 "0001 203040506.07",
4513                 "000102 3040506.07",
4514                 "00010203 40506.07",
4515                 "0001020304 506.07",
4516                 "000102030405 6.07",
4517                 "00010203040506. 7",
4518                 "001 0203040506.07",
4519                 "00012 03040506.07",
4520                 "0001023 040506.07",
4521                 "000102034 0506.07",
4522                 "00010203045 06.07",
4523                 "0001020304056 .07",
4524                 "00010203040506.7 ",
4525                 "00010203040506.",
4526                 "0001020304050607",
4527
4528                 "-0010203040506",
4529                 "0001-203040506",
4530                 "000102-3040506",
4531                 "00010203-40506",
4532                 "0001020304-506",
4533                 "000102030405-6",
4534                 "0001+203040506",
4535                 "000102+3040506",
4536                 "00010203+40506",
4537                 "0001020304+506",
4538                 "000102030405+6",
4539                 " 0010203040506",
4540                 "0001 203040506",
4541                 "000102 3040506",
4542                 "00010203 40506",
4543                 "0001020304 506",
4544                 "000102030405 6",
4545                 "001 0203040506",
4546                 "00012 03040506",
4547                 "0001023 040506",
4548                 "000102034 0506",
4549                 "00010203045 06",
4550                 "0001020304056 ",
4551
4552                 "-00102030405.07",
4553                 "0001-2030405.07",
4554                 "000102-30405.07",
4555                 "00010203-405.07",
4556                 "0001020304-5.07",
4557                 "000102030405.-7",
4558                 "+00102030405.07",
4559                 "0001+2030405.07",
4560                 "00010203+405.07",
4561                 "0001020304+5.07",
4562                 "000102030405.+7",
4563                 " 00102030405.07",
4564                 "0001 2030405.07",
4565                 "000102 30405.07",
4566                 "00010203 405.07",
4567                 "0001020304 5.07",
4568                 "000102030405. 7",
4569                 "001 02030405.07",
4570                 "00012 030405.07",
4571                 "0001023 0405.07",
4572                 "000102034 05.07",
4573                 "00010203045 .07",
4574                 "000102030405.7 ",
4575                 "000102030405.",
4576
4577                 "-001020304.07",
4578                 "0001-20304.07",
4579                 "000102-304.07",
4580                 "00010203-4.07",
4581                 "0001020304.-7",
4582                 "+001020304.07",
4583                 "0001+20304.07",
4584                 "00010203+4.07",
4585                 "0001020304.+7",
4586                 " 001020304.07",
4587                 "0001 20304.07",
4588                 "000102 304.07",
4589                 "00010203 4.07",
4590                 "0001020304. 7",
4591                 "001 020304.07",
4592                 "00012 0304.07",
4593                 "0001023 04.07",
4594                 "000102034 .07",
4595                 "0001020304.7 ",
4596                 "0001020304.",
4597
4598                 "00010203",
4599                 "00010203040506Y",
4600                 "0001010100+0001",
4601                 "0001010100+00:01",
4602                 "0001010100+01",
4603
4604                 "00010203040506.07+15",
4605                 "00010203040506.07-15",
4606                 "00010203040506.07+14:60",
4607                 "00010203040506.07+1460",
4608                 "00010203040506.07-1460",
4609                 "00010203040506.07+00:60",
4610                 "00010203040506.07-00:60",
4611
4612                 "00010203040506+15",
4613                 "00010203040506-15",
4614                 "00010203040506+14:60",
4615                 "00010203040506+1460",
4616                 "00010203040506-1460",
4617                 "00010203040506+00:60",
4618                 "00010203040506-00:60",
4619
4620                 "0001020304050.07",
4621                 "00010203040.07",
4622                 "000102030.07",
4623                 "0001020304050",
4624                 "00010203040",
4625                 "000102030",
4626         )):
4627             with self.assertRaises(DecodeError):
4628                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4629             data = data.replace(".", ",")
4630             with self.assertRaises(DecodeError):
4631                 GeneralizedTime(data.encode("ascii"), ctx={"bered": True})
4632
4633     @given(
4634         binary(
4635             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4636             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4637         ),
4638         binary(min_size=1, max_size=1),
4639         binary(
4640             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4641             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
4642         ),
4643     )
4644     def test_junk(self, part0, part1, part2):
4645         junk = part0 + part1 + part2
4646         assume(not (set(junk) <= set(digits.encode("ascii"))))
4647         with self.assertRaises(DecodeError):
4648             GeneralizedTime().decode(
4649                 GeneralizedTime.tag_default +
4650                 len_encode(len(junk)) +
4651                 junk
4652             )
4653
4654     @given(
4655         binary(
4656             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4657             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4658         ),
4659         binary(min_size=1, max_size=1),
4660         binary(
4661             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4662             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4663         ),
4664     )
4665     def test_junk_dm(self, part0, part1, part2):
4666         junk = part0 + part1 + part2
4667         assume(not (set(junk) <= set(digits.encode("ascii"))))
4668         with self.assertRaises(DecodeError):
4669             GeneralizedTime().decode(
4670                 GeneralizedTime.tag_default +
4671                 len_encode(len(junk)) +
4672                 junk
4673             )
4674
4675     def test_ns_fractions(self):
4676         GeneralizedTime(b"20010101000000.000001Z")
4677         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
4678             GeneralizedTime(b"20010101000000.0000001Z")
4679
4680     def test_non_pure_integers(self):
4681         for data in ((
4682                 # b"20000102030405Z,
4683                 b"+2000102030405Z",
4684                 b"2000+102030405Z",
4685                 b"200001+2030405Z",
4686                 b"20000102+30405Z",
4687                 b"2000010203+405Z",
4688                 b"200001020304+5Z",
4689                 b"20000102030405.+6Z",
4690                 b"20000102030405.-6Z",
4691                 b"_2000102030405Z",
4692                 b"2000_102030405Z",
4693                 b"200001_2030405Z",
4694                 b"20000102_30405Z",
4695                 b"2000010203_405Z",
4696                 b"200001020304_5Z",
4697                 b"20000102030405._6Z",
4698                 b"20000102030405.6_Z",
4699                 b" 2000102030405Z",
4700                 b"2000 102030405Z",
4701                 b"200001 2030405Z",
4702                 b"20000102 30405Z",
4703                 b"2000010203 405Z",
4704                 b"200001020304 5Z",
4705                 b"20000102030405. 6Z",
4706                 b"200 0102030405Z",
4707                 b"20001 02030405Z",
4708                 b"2000012 030405Z",
4709                 b"200001023 0405Z",
4710                 b"20000102034 05Z",
4711                 b"2000010203045 Z",
4712                 b"20000102030405.6 Z",
4713         )):
4714             with self.assertRaises(DecodeError):
4715                 GeneralizedTime(data)
4716
4717
4718 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
4719     base_klass = UTCTime
4720     omit_ms = True
4721     min_datetime = datetime(2000, 1, 1)
4722     max_datetime = datetime(2049, 12, 31)
4723     evgen_mode_skip_value = False
4724
4725     def additional_symmetric_check(self, value, obj_encoded):
4726         pass
4727
4728     def test_repr_not_ready(self):
4729         unicode(GeneralizedTime()) if PY2 else str(GeneralizedTime())
4730         repr(UTCTime())
4731
4732     def test_x690_vector_valid(self):
4733         for data in ((
4734                 b"920521000000Z",
4735                 b"920622123421Z",
4736                 b"920722132100Z",
4737         )):
4738             UTCTime(data)
4739
4740     def test_x690_vector_invalid(self):
4741         for data in ((
4742                 b"920520240000Z",
4743                 b"9207221321Z",
4744         )):
4745             with self.assertRaises(DecodeError) as err:
4746                 UTCTime(data)
4747             repr(err.exception)
4748
4749     def test_go_vectors_invalid(self):
4750         for data in ((
4751                 b"a10506234540Z",
4752                 b"91a506234540Z",
4753                 b"9105a6234540Z",
4754                 b"910506a34540Z",
4755                 b"910506334a40Z",
4756                 b"91050633444aZ",
4757                 b"910506334461Z",
4758                 b"910506334400Za",
4759                 b"000100000000Z",
4760                 b"101302030405Z",
4761                 b"100002030405Z",
4762                 b"100100030405Z",
4763                 b"100132030405Z",
4764                 b"100231030405Z",
4765                 b"100102240405Z",
4766                 b"100102036005Z",
4767                 b"100102030460Z",
4768                 b"-100102030410Z",
4769                 b"10-0102030410Z",
4770                 b"10-0002030410Z",
4771                 b"1001-02030410Z",
4772                 b"100102-030410Z",
4773                 b"10010203-0410Z",
4774                 b"1001020304-10Z",
4775                 # These ones are INVALID in *DER*, but accepted
4776                 # by Go's encoding/asn1
4777                 b"910506164540-0700",
4778                 b"910506164540+0730",
4779                 b"9105062345Z",
4780                 b"5105062345Z",
4781         )):
4782             with self.assertRaises(DecodeError) as err:
4783                 UTCTime(data)
4784             repr(err.exception)
4785
4786     def test_go_vectors_valid(self):
4787         self.assertEqual(
4788             UTCTime(b"910506234540Z").todatetime(),
4789             datetime(1991, 5, 6, 23, 45, 40, 0),
4790         )
4791
4792     def test_non_pure_integers(self):
4793         for data in ((
4794                 # b"000102030405Z",
4795                 b"+10102030405Z",
4796                 b"00+102030405Z",
4797                 b"0001+2030405Z",
4798                 b"000102+30405Z",
4799                 b"00010203+405Z",
4800                 b"0001020304+5Z",
4801                 b"_10102030405Z",
4802                 b"00_102030405Z",
4803                 b"0001_2030405Z",
4804                 b"000102_30405Z",
4805                 b"00010203_405Z",
4806                 b"0001020304_5Z",
4807                 b"00010203045_Z",
4808                 b" 10102030405Z",
4809                 b"00 102030405Z",
4810                 b"0001 2030405Z",
4811                 b"000102 30405Z",
4812                 b"00010203 405Z",
4813                 b"0001020304 5Z",
4814                 b"1 0102030405Z",
4815                 b"001 02030405Z",
4816                 b"00012 030405Z",
4817                 b"0001023 0405Z",
4818                 b"000102034 05Z",
4819                 b"00010203045 Z",
4820         )):
4821             with self.assertRaises(DecodeError):
4822                 UTCTime(data)
4823
4824     def test_x680_vector_valid_ber(self):
4825         for data, dt in ((
4826                 (b"8201021200Z", datetime(1982, 1, 2, 12)),
4827                 (b"8201020700-0500", datetime(1982, 1, 2, 12)),
4828                 (b"0101021200Z", datetime(2001, 1, 2, 12)),
4829                 (b"0101020700-0500", datetime(2001, 1, 2, 12)),
4830         )):
4831             data_der = UTCTime.tag_default + len_encode(len(data)) + data
4832             obj = UTCTime().decod(data_der, ctx={"bered": True})
4833             self.assertEqual(obj, dt)
4834             self.assertEqual(obj.todatetime(), dt)
4835             self.assertTrue(obj.ber_encoded)
4836             self.assertTrue(obj.bered)
4837             self.assertEqual(obj.ber_raw, data)
4838             self.assertNotEqual(obj.encode(), data_der)
4839             repr(obj)
4840
4841     def test_go_vectors_valid_ber(self):
4842         for data in ((
4843                 b"910506164540-0700",
4844                 b"910506164540+0730",
4845                 b"9105062345Z",
4846                 b"5105062345Z",
4847         )):
4848             data = UTCTime.tag_default + len_encode(len(data)) + data
4849             obj = UTCTime().decod(data, ctx={"bered": True})
4850             self.assertTrue(obj.ber_encoded)
4851             self.assertTrue(obj.bered)
4852             self.assertNotEqual(obj.encode(), data)
4853             repr(obj)
4854
4855     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4856     @given(data_strategy())
4857     def test_valid_ber(self, d):
4858         year = d.draw(integers(min_value=0, max_value=99))
4859         month = d.draw(integers(min_value=1, max_value=12))
4860         day = d.draw(integers(min_value=1, max_value=28))
4861         hours = d.draw(integers(min_value=0, max_value=23))
4862         minute = d.draw(integers(min_value=0, max_value=59))
4863         data = "%02d%02d%02d%02d%02d" % (year, month, day, hours, minute)
4864         dt = datetime(
4865             year + (2000 if year < 50 else 1900),
4866             month,
4867             day,
4868             hours,
4869             minute,
4870         )
4871         dered = False
4872         if d.draw(booleans()):
4873             dered = True
4874             seconds = d.draw(integers(min_value=0, max_value=59))
4875             data += "%02d" % seconds
4876             dt += timedelta(seconds=seconds)
4877         if d.draw(booleans()):
4878             data += "Z"
4879         else:
4880             dered = False
4881             offset_hour = d.draw(integers(min_value=0, max_value=13))
4882             offset_minute = d.draw(integers(min_value=0, max_value=59))
4883             offset = timedelta(seconds=offset_hour * 3600 + offset_minute * 60)
4884             if d.draw(booleans()):
4885                 dt += offset
4886                 data += "-"
4887             else:
4888                 dt -= offset
4889                 data += "+"
4890             data += "%02d%02d" % (offset_hour, offset_minute)
4891         data = data.encode("ascii")
4892         data_der = UTCTime.tag_default + len_encode(len(data)) + data
4893         obj = UTCTime().decod(data_der, ctx={"bered": True})
4894         self.assertEqual(obj, dt)
4895         self.assertEqual(obj.todatetime(), dt)
4896         self.assertEqual(obj.ber_encoded, not dered)
4897         self.assertEqual(obj.bered, not dered)
4898         self.assertEqual(obj.ber_raw, None if dered else data)
4899         self.assertEqual(obj.encode() == data_der, dered)
4900         repr(obj)
4901         bytes(obj)
4902         str(obj)
4903
4904     def test_invalid_ber(self):
4905         for data in ((
4906                 # b"0001020304Z",
4907                 b"-101020304Z",
4908                 b"00-1020304Z",
4909                 b"0001-20304Z",
4910                 b"000102-304Z",
4911                 b"000102-104Z",
4912                 b"00000203-4Z",
4913                 b"+101020304Z",
4914                 b"00+1020304Z",
4915                 b"0001+20304Z",
4916                 b"000102+304Z",
4917                 b"000102+104Z",
4918                 b"00000203+4Z",
4919                 b" 101020304Z",
4920                 b"00 1020304Z",
4921                 b"0001 20304Z",
4922                 b"000102 304Z",
4923                 b"000102 104Z",
4924                 b"00000203 4Z",
4925                 b"1 01020304Z",
4926                 b"001 020304Z",
4927                 b"00012 0304Z",
4928                 b"0001023 04Z",
4929                 b"0001021 04Z",
4930                 b"000002034 Z",
4931                 b"0013020304Z",
4932                 b"0001000304Z",
4933                 b"0001320304Z",
4934                 b"0001022404Z",
4935                 b"0001020360Z",
4936                 b"0002300304Z",
4937                 b"0001020304",
4938                 b"0001020304T",
4939                 b"0001020304+",
4940                 b"0001020304-",
4941                 b"0001020304+0",
4942                 b"0001020304+00",
4943                 b"0001020304+000",
4944                 b"0001020304+000Z",
4945                 b"0001020304+0000Z",
4946                 b"0001020304+-101",
4947                 b"0001020304+01-1",
4948                 b"0001020304+0060",
4949                 b"0001020304+1401",
4950                 b"5001010000+0001",
4951                 b"000102030Z",
4952                 b"0001020Z",
4953         )):
4954             with self.assertRaises(DecodeError):
4955                 UTCTime(data, ctx={"bered": True})
4956             data = data[:8] + data[8+2:]
4957             with self.assertRaises(DecodeError):
4958                 UTCTime(data, ctx={"bered": True})
4959
4960         for data in ((
4961                 # b"000102030405Z",
4962                 b"-10102030405Z",
4963                 b"00-102030405Z",
4964                 b"0001-2030405Z",
4965                 b"000102-30405Z",
4966                 b"000102-10405Z",
4967                 b"00000203-405Z",
4968                 b"0000020304-5Z",
4969                 b"+10102030405Z",
4970                 b"00+102030405Z",
4971                 b"0001+2030405Z",
4972                 b"000102+30405Z",
4973                 b"000102+10405Z",
4974                 b"00000203+405Z",
4975                 b"0000020304+5Z",
4976                 b" 10102030405Z",
4977                 b"00 102030405Z",
4978                 b"0001 2030405Z",
4979                 b"000102 30405Z",
4980                 b"000102 10405Z",
4981                 b"00000203 405Z",
4982                 b"0000020304 5Z",
4983                 b"1 0102030405Z",
4984                 b"001 02030405Z",
4985                 b"00012 030405Z",
4986                 b"0001023 0405Z",
4987                 b"0001021 0405Z",
4988                 b"000002034 05Z",
4989                 b"00000203045 Z",
4990                 b"001302030405Z",
4991                 b"000100030405Z",
4992                 b"000132030405Z",
4993                 b"000102240405Z",
4994                 b"000102036005Z",
4995                 b"000230030405Z",
4996                 b"000102030460Z",
4997                 b"000102030405",
4998                 b"000102030405T",
4999                 b"000102030405+",
5000                 b"000102030405-",
5001                 b"000102030405+0",
5002                 b"000102030405+00",
5003                 b"000102030405+000",
5004                 b"000102030405+000Z",
5005                 b"000102030405+0000Z",
5006                 b"000102030405+-101",
5007                 b"000102030405+01-1",
5008                 b"000102030405+0060",
5009                 b"000102030405+1401",
5010                 b"500101000002+0003",
5011         )):
5012             with self.assertRaises(DecodeError):
5013                 UTCTime(data, ctx={"bered": True})
5014
5015     @given(integers(min_value=0, max_value=49))
5016     def test_pre50(self, year):
5017         self.assertEqual(
5018             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
5019             2000 + year,
5020         )
5021
5022     @given(integers(min_value=50, max_value=99))
5023     def test_post50(self, year):
5024         self.assertEqual(
5025             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
5026             1900 + year,
5027         )
5028
5029     @given(
5030         binary(
5031             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5032             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5033         ),
5034         binary(min_size=1, max_size=1),
5035         binary(
5036             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5037             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
5038         ),
5039     )
5040     def test_junk(self, part0, part1, part2):
5041         junk = part0 + part1 + part2
5042         assume(not (set(junk) <= set(digits.encode("ascii"))))
5043         with self.assertRaises(DecodeError):
5044             UTCTime().decode(
5045                 UTCTime.tag_default +
5046                 len_encode(len(junk)) +
5047                 junk
5048             )
5049
5050
5051 @composite
5052 def tlv_value_strategy(draw):
5053     tag_num = draw(integers(min_value=1))
5054     data = draw(binary())
5055     return b"".join((tag_encode(tag_num), len_encode(len(data)), data))
5056
5057
5058 @composite
5059 def any_values_strategy(draw, do_expl=False):
5060     value = draw(one_of(none(), tlv_value_strategy()))
5061     expl = None
5062     if do_expl:
5063         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5064     optional = draw(one_of(none(), booleans()))
5065     _decoded = (
5066         draw(integers(min_value=0)),
5067         draw(integers(min_value=0)),
5068         draw(integers(min_value=0)),
5069     )
5070     return (value, expl, optional, _decoded)
5071
5072
5073 class AnyInherited(Any):
5074     pass
5075
5076
5077 class TestAny(CommonMixin, TestCase):
5078     base_klass = Any
5079
5080     def test_invalid_value_type(self):
5081         with self.assertRaises(InvalidValueType) as err:
5082             Any(123)
5083         repr(err.exception)
5084
5085     @given(booleans())
5086     def test_optional(self, optional):
5087         obj = Any(optional=optional)
5088         self.assertEqual(obj.optional, optional)
5089
5090     @given(tlv_value_strategy())
5091     def test_ready(self, value):
5092         obj = Any()
5093         self.assertFalse(obj.ready)
5094         repr(obj)
5095         list(obj.pps())
5096         pprint(obj, big_blobs=True, with_decode_path=True)
5097         with self.assertRaises(ObjNotReady) as err:
5098             obj.encode()
5099         repr(err.exception)
5100         with self.assertRaises(ObjNotReady) as err:
5101             encode2pass(obj)
5102         obj = Any(value)
5103         self.assertTrue(obj.ready)
5104         repr(obj)
5105         list(obj.pps())
5106         pprint(obj, big_blobs=True, with_decode_path=True)
5107
5108     @given(integers())
5109     def test_basic(self, value):
5110         integer_encoded = Integer(value).encode()
5111         for obj in (
5112                 Any(integer_encoded),
5113                 Any(Integer(value)),
5114                 Any(Any(Integer(value))),
5115         ):
5116             self.assertSequenceEqual(bytes(obj), integer_encoded)
5117             self.assertEqual(
5118                 obj.decode(obj.encode())[0].vlen,
5119                 len(integer_encoded),
5120             )
5121             repr(obj)
5122             list(obj.pps())
5123             pprint(obj, big_blobs=True, with_decode_path=True)
5124             self.assertSequenceEqual(obj.encode(), integer_encoded)
5125
5126     @given(tlv_value_strategy(), tlv_value_strategy())
5127     def test_comparison(self, value1, value2):
5128         for klass in (Any, AnyInherited):
5129             obj1 = klass(value1)
5130             obj2 = klass(value2)
5131             self.assertEqual(obj1 == obj2, value1 == value2)
5132             self.assertEqual(obj1 != obj2, value1 != value2)
5133             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
5134
5135     @given(data_strategy())
5136     def test_call(self, d):
5137         for klass in (Any, AnyInherited):
5138             (
5139                 value_initial,
5140                 expl_initial,
5141                 optional_initial,
5142                 _decoded_initial,
5143             ) = d.draw(any_values_strategy())
5144             obj_initial = klass(
5145                 value_initial,
5146                 expl_initial,
5147                 optional_initial or False,
5148                 _decoded_initial,
5149             )
5150             (
5151                 value,
5152                 expl,
5153                 optional,
5154                 _decoded,
5155             ) = d.draw(any_values_strategy(do_expl=True))
5156             obj = obj_initial(value, expl, optional)
5157             if obj.ready:
5158                 value_expected = None if value is None else value
5159                 self.assertEqual(obj, value_expected)
5160             self.assertEqual(obj.expl_tag, expl or expl_initial)
5161             if obj.default is None:
5162                 optional = optional_initial if optional is None else optional
5163                 optional = False if optional is None else optional
5164             self.assertEqual(obj.optional, optional)
5165
5166     def test_simultaneous_impl_expl(self):
5167         # override it, as Any does not have implicit tag
5168         pass
5169
5170     def test_decoded(self):
5171         # override it, as Any does not have implicit tag
5172         pass
5173
5174     @given(any_values_strategy())
5175     def test_copy(self, values):
5176         for klass in (Any, AnyInherited):
5177             obj = klass(*values)
5178             for copy_func in copy_funcs:
5179                 obj_copied = copy_func(obj)
5180                 self.assert_copied_basic_fields(obj, obj_copied)
5181                 self.assertEqual(obj._value, obj_copied._value)
5182
5183     @given(binary().map(OctetString))
5184     def test_stripped(self, value):
5185         obj = Any(value)
5186         with self.assertRaises(NotEnoughData):
5187             obj.decode(obj.encode()[:-1])
5188
5189     @given(
5190         tlv_value_strategy(),
5191         integers(min_value=1).map(tag_ctxc),
5192     )
5193     def test_stripped_expl(self, value, tag_expl):
5194         obj = Any(value, expl=tag_expl)
5195         with self.assertRaises(NotEnoughData):
5196             obj.decode(obj.encode()[:-1])
5197
5198     @given(
5199         integers(min_value=31),
5200         integers(min_value=0),
5201         decode_path_strat,
5202     )
5203     def test_bad_tag(self, tag, offset, decode_path):
5204         with self.assertRaises(DecodeError) as err:
5205             Any().decode(
5206                 tag_encode(tag)[:-1],
5207                 offset=offset,
5208                 decode_path=decode_path,
5209             )
5210         repr(err.exception)
5211         self.assertEqual(err.exception.offset, offset)
5212         self.assertEqual(err.exception.decode_path, decode_path)
5213
5214     @given(
5215         integers(min_value=128),
5216         integers(min_value=0),
5217         decode_path_strat,
5218     )
5219     def test_bad_len(self, l, offset, decode_path):
5220         with self.assertRaises(DecodeError) as err:
5221             Any().decode(
5222                 Any.tag_default + len_encode(l)[:-1],
5223                 offset=offset,
5224                 decode_path=decode_path,
5225             )
5226         repr(err.exception)
5227         self.assertEqual(err.exception.offset, offset)
5228         self.assertEqual(err.exception.decode_path, decode_path)
5229
5230     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5231     @given(
5232         any_values_strategy(),
5233         integers().map(lambda x: Integer(x).encode()),
5234         integers(min_value=1).map(tag_ctxc),
5235         integers(min_value=0),
5236         binary(max_size=5),
5237         decode_path_strat,
5238     )
5239     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
5240         for klass in (Any, AnyInherited):
5241             _, _, optional, _decoded = values
5242             obj = klass(value=value, optional=optional, _decoded=_decoded)
5243             repr(obj)
5244             list(obj.pps())
5245             pprint(obj, big_blobs=True, with_decode_path=True)
5246             self.assertFalse(obj.expled)
5247             tag_class, _, tag_num = tag_decode(tag_strip(value)[0])
5248             self.assertEqual(obj.tag_order, (tag_class, tag_num))
5249             obj_encoded = obj.encode()
5250             self.assertEqual(encode2pass(obj), obj_encoded)
5251             obj_expled = obj(value, expl=tag_expl)
5252             self.assertTrue(obj_expled.expled)
5253             tag_class, _, tag_num = tag_decode(tag_expl)
5254             self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5255             repr(obj_expled)
5256             list(obj_expled.pps())
5257             pprint(obj_expled, big_blobs=True, with_decode_path=True)
5258             obj_expled_encoded = obj_expled.encode()
5259             ctx_copied = deepcopy(ctx_dummy)
5260             obj_decoded, tail = obj_expled.decode(
5261                 obj_expled_encoded + tail_junk,
5262                 offset=offset,
5263                 ctx=ctx_copied,
5264             )
5265             self.assertDictEqual(ctx_copied, ctx_dummy)
5266             repr(obj_decoded)
5267             list(obj_decoded.pps())
5268             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5269             self.assertEqual(tail, tail_junk)
5270             self.assertEqual(obj_decoded, obj_expled)
5271             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
5272             self.assertEqual(bytes(obj_decoded), bytes(obj))
5273             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5274             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5275             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5276             self.assertEqual(
5277                 obj_decoded.expl_llen,
5278                 len(len_encode(len(obj_encoded))),
5279             )
5280             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5281             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5282             self.assertEqual(
5283                 obj_decoded.offset,
5284                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5285             )
5286             self.assertEqual(obj_decoded.expl_offset, offset)
5287             self.assertEqual(obj_decoded.tlen, 0)
5288             self.assertEqual(obj_decoded.llen, 0)
5289             self.assertEqual(obj_decoded.vlen, len(value))
5290             assert_exceeding_data(
5291                 self,
5292                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5293                 tail_junk,
5294             )
5295
5296             evgens = list(obj_expled.decode_evgen(
5297                 obj_expled_encoded + tail_junk,
5298                 offset=offset,
5299                 decode_path=decode_path,
5300                 ctx=ctx_copied,
5301             ))
5302             self.assertEqual(len(evgens), 1)
5303             _decode_path, obj, tail = evgens[0]
5304             self.assertSequenceEqual(tail, tail_junk)
5305             self.assertEqual(_decode_path, decode_path)
5306             self.assertEqual(obj.expl_offset, offset)
5307             repr(obj)
5308             list(obj.pps())
5309
5310     @given(
5311         integers(min_value=1).map(tag_ctxc),
5312         integers(min_value=0, max_value=3),
5313         integers(min_value=0),
5314         decode_path_strat,
5315         binary(),
5316     )
5317     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
5318         chunk = Boolean(False, expl=expl).encode()
5319         encoded = (
5320             OctetString.tag_default +
5321             LENINDEF +
5322             b"".join([chunk] * chunks) +
5323             EOC
5324         )
5325         with self.assertRaises(LenIndefForm):
5326             Any().decode(
5327                 encoded + junk,
5328                 offset=offset,
5329                 decode_path=decode_path,
5330             )
5331         obj, tail = Any().decode(
5332             encoded + junk,
5333             offset=offset,
5334             decode_path=decode_path,
5335             ctx={"bered": True},
5336         )
5337         self.assertSequenceEqual(tail, junk)
5338         self.assertEqual(obj.offset, offset)
5339         self.assertEqual(obj.tlvlen, len(encoded))
5340         self.assertTrue(obj.lenindef)
5341         self.assertFalse(obj.ber_encoded)
5342         self.assertTrue(obj.bered)
5343         obj = copy(obj)
5344         self.assertTrue(obj.lenindef)
5345         self.assertFalse(obj.ber_encoded)
5346         self.assertTrue(obj.bered)
5347         repr(obj)
5348         list(obj.pps())
5349         pprint(obj, big_blobs=True, with_decode_path=True)
5350         with self.assertRaises(NotEnoughData) as err:
5351             Any().decode(
5352                 encoded[:-1],
5353                 offset=offset,
5354                 decode_path=decode_path,
5355                 ctx={"bered": True},
5356             )
5357         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
5358         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
5359
5360         class SeqOf(SequenceOf):
5361             schema = Boolean(expl=expl)
5362
5363         class Seq(Sequence):
5364             schema = (
5365                 ("type", ObjectIdentifier(defines=((("value",), {
5366                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
5367                 }),))),
5368                 ("value", Any()),
5369             )
5370         seq = Seq((
5371             ("type", ObjectIdentifier("1.2.3")),
5372             ("value", Any(encoded)),
5373         ))
5374         seq_encoded = seq.encode()
5375         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5376         self.assertIsNotNone(seq_decoded["value"].defined)
5377         repr(seq_decoded)
5378         list(seq_decoded.pps())
5379         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5380         self.assertTrue(seq_decoded.bered)
5381         self.assertFalse(seq_decoded["type"].bered)
5382         self.assertTrue(seq_decoded["value"].bered)
5383
5384         chunk = chunk[:-1] + b"\x01"
5385         chunks = b"".join([chunk] * (chunks + 1))
5386         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
5387         seq = Seq((
5388             ("type", ObjectIdentifier("1.2.3")),
5389             ("value", Any(encoded)),
5390         ))
5391         seq_encoded = seq.encode()
5392         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
5393         self.assertIsNotNone(seq_decoded["value"].defined)
5394         repr(seq_decoded)
5395         list(seq_decoded.pps())
5396         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
5397         self.assertTrue(seq_decoded.bered)
5398         self.assertFalse(seq_decoded["type"].bered)
5399         self.assertTrue(seq_decoded["value"].bered)
5400
5401
5402 @composite
5403 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
5404     if schema is None:
5405         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
5406         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
5407             one_of(
5408                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
5409                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
5410             ),
5411             min_size=len(names),
5412             max_size=len(names),
5413         ))]
5414         schema = [
5415             (name, Integer(**tag_kwargs))
5416             for name, tag_kwargs in zip(names, tags)
5417         ]
5418     value = None
5419     if value_required or draw(booleans()):
5420         value = draw(tuples(
5421             sampled_from([name for name, _ in schema]),
5422             integers().map(Integer),
5423         ))
5424     expl = None
5425     if do_expl:
5426         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5427     default = draw(one_of(
5428         none(),
5429         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
5430     ))
5431     optional = draw(one_of(none(), booleans()))
5432     _decoded = (
5433         draw(integers(min_value=0)),
5434         draw(integers(min_value=0)),
5435         draw(integers(min_value=0)),
5436     )
5437     return (schema, value, expl, default, optional, _decoded)
5438
5439
5440 class ChoiceInherited(Choice):
5441     pass
5442
5443
5444 class TestChoice(CommonMixin, TestCase):
5445     class Wahl(Choice):
5446         schema = (("whatever", Boolean()),)
5447     base_klass = Wahl
5448
5449     def test_schema_required(self):
5450         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5451             Choice()
5452
5453     def test_impl_forbidden(self):
5454         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
5455             Choice(impl=b"whatever")
5456
5457     def test_invalid_value_type(self):
5458         with self.assertRaises(InvalidValueType) as err:
5459             self.base_klass(123)
5460         repr(err.exception)
5461         with self.assertRaises(ObjUnknown) as err:
5462             self.base_klass(("whenever", Boolean(False)))
5463         repr(err.exception)
5464         with self.assertRaises(InvalidValueType) as err:
5465             self.base_klass(("whatever", Integer(123)))
5466         repr(err.exception)
5467
5468     @given(booleans())
5469     def test_optional(self, optional):
5470         obj = self.base_klass(
5471             default=self.base_klass(("whatever", Boolean(False))),
5472             optional=optional,
5473         )
5474         self.assertTrue(obj.optional)
5475
5476     @given(booleans())
5477     def test_ready(self, value):
5478         obj = self.base_klass()
5479         self.assertFalse(obj.ready)
5480         repr(obj)
5481         list(obj.pps())
5482         pprint(obj, big_blobs=True, with_decode_path=True)
5483         self.assertIsNone(obj["whatever"])
5484         with self.assertRaises(ObjNotReady) as err:
5485             obj.encode()
5486         repr(err.exception)
5487         with self.assertRaises(ObjNotReady) as err:
5488             encode2pass(obj)
5489         obj["whatever"] = Boolean()
5490         self.assertFalse(obj.ready)
5491         repr(obj)
5492         list(obj.pps())
5493         pprint(obj, big_blobs=True, with_decode_path=True)
5494         obj["whatever"] = Boolean(value)
5495         self.assertTrue(obj.ready)
5496         repr(obj)
5497         list(obj.pps())
5498         pprint(obj, big_blobs=True, with_decode_path=True)
5499
5500     @given(booleans(), booleans())
5501     def test_comparison(self, value1, value2):
5502         class WahlInherited(self.base_klass):
5503             pass
5504         for klass in (self.base_klass, WahlInherited):
5505             obj1 = klass(("whatever", Boolean(value1)))
5506             obj2 = klass(("whatever", Boolean(value2)))
5507             self.assertEqual(obj1 == obj2, value1 == value2)
5508             self.assertEqual(obj1 != obj2, value1 != value2)
5509             self.assertEqual(obj1 == obj2._value, value1 == value2)
5510             self.assertFalse(obj1 == obj2._value[1])
5511
5512     @given(data_strategy())
5513     def test_call(self, d):
5514         for klass in (Choice, ChoiceInherited):
5515             (
5516                 schema_initial,
5517                 value_initial,
5518                 expl_initial,
5519                 default_initial,
5520                 optional_initial,
5521                 _decoded_initial,
5522             ) = d.draw(choice_values_strategy())
5523
5524             class Wahl(klass):
5525                 schema = schema_initial
5526             obj_initial = Wahl(
5527                 value=value_initial,
5528                 expl=expl_initial,
5529                 default=default_initial,
5530                 optional=optional_initial or False,
5531                 _decoded=_decoded_initial,
5532             )
5533             (
5534                 _,
5535                 value,
5536                 expl,
5537                 default,
5538                 optional,
5539                 _decoded,
5540             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
5541             obj = obj_initial(value, expl, default, optional)
5542             if obj.ready:
5543                 value_expected = default if value is None else value
5544                 value_expected = (
5545                     default_initial if value_expected is None
5546                     else value_expected
5547                 )
5548                 self.assertEqual(obj.choice, value_expected[0])
5549                 self.assertEqual(obj.value, int(value_expected[1]))
5550             self.assertEqual(obj.expl_tag, expl or expl_initial)
5551             default_expect = default_initial if default is None else default
5552             if default_expect is not None:
5553                 self.assertEqual(obj.default.choice, default_expect[0])
5554                 self.assertEqual(obj.default.value, int(default_expect[1]))
5555             if obj.default is None:
5556                 optional = optional_initial if optional is None else optional
5557                 optional = False if optional is None else optional
5558             else:
5559                 optional = True
5560             self.assertEqual(obj.optional, optional)
5561             self.assertEqual(obj.specs, obj_initial.specs)
5562
5563     def test_simultaneous_impl_expl(self):
5564         # override it, as Any does not have implicit tag
5565         pass
5566
5567     def test_decoded(self):
5568         # override it, as Any does not have implicit tag
5569         pass
5570
5571     @given(choice_values_strategy())
5572     def test_copy(self, values):
5573         _schema, value, expl, default, optional, _decoded = values
5574
5575         class Wahl(self.base_klass):
5576             schema = _schema
5577         register_class(Wahl)
5578         obj = Wahl(
5579             value=value,
5580             expl=expl,
5581             default=default,
5582             optional=optional or False,
5583             _decoded=_decoded,
5584         )
5585         for copy_func in copy_funcs:
5586             obj_copied = copy_func(obj)
5587             self.assertIsNone(obj.tag)
5588             self.assertIsNone(obj_copied.tag)
5589             # hack for assert_copied_basic_fields
5590             obj.tag = "whatever"
5591             obj_copied.tag = "whatever"
5592             self.assert_copied_basic_fields(obj, obj_copied)
5593             obj.tag = None
5594             self.assertEqual(obj._value, obj_copied._value)
5595             self.assertEqual(obj.specs, obj_copied.specs)
5596
5597     @given(booleans())
5598     def test_stripped(self, value):
5599         obj = self.base_klass(("whatever", Boolean(value)))
5600         with self.assertRaises(NotEnoughData):
5601             obj.decode(obj.encode()[:-1])
5602
5603     @given(
5604         booleans(),
5605         integers(min_value=1).map(tag_ctxc),
5606     )
5607     def test_stripped_expl(self, value, tag_expl):
5608         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
5609         with self.assertRaises(NotEnoughData):
5610             obj.decode(obj.encode()[:-1])
5611
5612     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5613     @given(data_strategy())
5614     def test_symmetric(self, d):
5615         _schema, value, _, default, optional, _decoded = d.draw(
5616             choice_values_strategy(value_required=True)
5617         )
5618         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5619         offset = d.draw(integers(min_value=0))
5620         tail_junk = d.draw(binary(max_size=5))
5621         decode_path = d.draw(decode_path_strat)
5622
5623         class Wahl(self.base_klass):
5624             schema = _schema
5625         obj = Wahl(
5626             value=value,
5627             default=default,
5628             optional=optional,
5629             _decoded=_decoded,
5630         )
5631         repr(obj)
5632         list(obj.pps())
5633         pprint(obj, big_blobs=True, with_decode_path=True)
5634         self.assertFalse(obj.expled)
5635         self.assertEqual(obj.tag_order, obj.value.tag_order)
5636         obj_encoded = obj.encode()
5637         self.assertEqual(encode2pass(obj), obj_encoded)
5638         obj_expled = obj(value, expl=tag_expl)
5639         self.assertTrue(obj_expled.expled)
5640         tag_class, _, tag_num = tag_decode(tag_expl)
5641         self.assertEqual(obj_expled.tag_order, (tag_class, tag_num))
5642         repr(obj_expled)
5643         list(obj_expled.pps())
5644         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5645         obj_expled_encoded = obj_expled.encode()
5646         ctx_copied = deepcopy(ctx_dummy)
5647         obj_decoded, tail = obj_expled.decode(
5648             obj_expled_encoded + tail_junk,
5649             offset=offset,
5650             ctx=ctx_copied,
5651         )
5652         self.assertDictEqual(ctx_copied, ctx_dummy)
5653         repr(obj_decoded)
5654         list(obj_decoded.pps())
5655         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5656         self.assertEqual(tail, tail_junk)
5657         self.assertEqual(obj_decoded, obj_expled)
5658         self.assertEqual(obj_decoded.choice, obj_expled.choice)
5659         self.assertEqual(obj_decoded.value, obj_expled.value)
5660         self.assertEqual(obj_decoded.choice, obj.choice)
5661         self.assertEqual(obj_decoded.value, obj.value)
5662         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5663         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5664         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5665         self.assertEqual(
5666             obj_decoded.expl_llen,
5667             len(len_encode(len(obj_encoded))),
5668         )
5669         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5670         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5671         self.assertEqual(
5672             obj_decoded.offset,
5673             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5674         )
5675         self.assertEqual(obj_decoded.expl_offset, offset)
5676         self.assertSequenceEqual(
5677             obj_expled_encoded[
5678                 obj_decoded.value.fulloffset - offset:
5679                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
5680             ],
5681             obj_encoded,
5682         )
5683         assert_exceeding_data(
5684             self,
5685             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5686             tail_junk,
5687         )
5688
5689         evgens = list(obj_expled.decode_evgen(
5690             obj_expled_encoded + tail_junk,
5691             offset=offset,
5692             decode_path=decode_path,
5693             ctx=ctx_copied,
5694         ))
5695         self.assertEqual(len(evgens), 2)
5696         _decode_path, obj, tail = evgens[0]
5697         self.assertEqual(_decode_path, decode_path + (obj_decoded.choice,))
5698         _decode_path, obj, tail = evgens[1]
5699         self.assertSequenceEqual(tail, tail_junk)
5700         self.assertEqual(_decode_path, decode_path)
5701         self.assertEqual(obj.expl_offset, offset)
5702         repr(obj)
5703         list(obj.pps())
5704
5705     @given(integers())
5706     def test_set_get(self, value):
5707         class Wahl(Choice):
5708             schema = (
5709                 ("erste", Boolean()),
5710                 ("zweite", Integer()),
5711             )
5712         obj = Wahl()
5713         with self.assertRaises(ObjUnknown) as err:
5714             obj["whatever"] = "whenever"
5715         with self.assertRaises(InvalidValueType) as err:
5716             obj["zweite"] = Boolean(False)
5717         obj["zweite"] = Integer(value)
5718         repr(err.exception)
5719         with self.assertRaises(ObjUnknown) as err:
5720             obj["whatever"]
5721         repr(err.exception)
5722         self.assertIsNone(obj["erste"])
5723         self.assertEqual(obj["zweite"], Integer(value))
5724
5725     def test_tag_mismatch(self):
5726         class Wahl(Choice):
5727             schema = (
5728                 ("erste", Boolean()),
5729             )
5730         int_encoded = Integer(123).encode()
5731         bool_encoded = Boolean(False).encode()
5732         obj = Wahl()
5733         obj.decode(bool_encoded)
5734         with self.assertRaises(TagMismatch):
5735             obj.decode(int_encoded)
5736
5737     def test_tag_mismatch_underlying(self):
5738         class SeqOfBoolean(SequenceOf):
5739             schema = Boolean()
5740
5741         class SeqOfInteger(SequenceOf):
5742             schema = Integer()
5743
5744         class Wahl(Choice):
5745             schema = (
5746                 ("erste", SeqOfBoolean()),
5747             )
5748
5749         int_encoded = SeqOfInteger((Integer(123),)).encode()
5750         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
5751         obj = Wahl()
5752         obj.decode(bool_encoded)
5753         with self.assertRaises(TagMismatch) as err:
5754             obj.decode(int_encoded)
5755         self.assertEqual(err.exception.decode_path, ("erste", "0"))
5756
5757
5758 @composite
5759 def seq_values_strategy(draw, seq_klass, do_expl=False):
5760     value = None
5761     if draw(booleans()):
5762         value = seq_klass()
5763         value._value = draw(dictionaries(
5764             integers(),
5765             one_of(
5766                 booleans().map(Boolean),
5767                 integers().map(Integer),
5768             ),
5769         ))
5770     schema = None
5771     if draw(booleans()):
5772         schema = list(draw(dictionaries(
5773             integers(),
5774             one_of(
5775                 booleans().map(Boolean),
5776                 integers().map(Integer),
5777             ),
5778         )).items())
5779     impl = None
5780     expl = None
5781     if do_expl:
5782         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5783     else:
5784         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5785     default = None
5786     if draw(booleans()):
5787         default = seq_klass()
5788         default._value = draw(dictionaries(
5789             integers(),
5790             one_of(
5791                 booleans().map(Boolean),
5792                 integers().map(Integer),
5793             ),
5794         ))
5795     optional = draw(one_of(none(), booleans()))
5796     _decoded = (
5797         draw(integers(min_value=0)),
5798         draw(integers(min_value=0)),
5799         draw(integers(min_value=0)),
5800     )
5801     return (value, schema, impl, expl, default, optional, _decoded)
5802
5803
5804 @composite
5805 def sequence_strategy(draw, seq_klass):
5806     inputs = draw(lists(
5807         one_of(
5808             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
5809             tuples(just(Integer), integers(), one_of(none(), integers())),
5810         ),
5811         max_size=6,
5812     ))
5813     tags = draw(sets(
5814         integers(min_value=1),
5815         min_size=len(inputs),
5816         max_size=len(inputs),
5817     ))
5818     inits = [
5819         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5820         for tag, expled in zip(tags, draw(lists(
5821             booleans(),
5822             min_size=len(inputs),
5823             max_size=len(inputs),
5824         )))
5825     ]
5826     empties = []
5827     for i, optional in enumerate(draw(lists(
5828             sampled_from(("required", "optional", "empty")),
5829             min_size=len(inputs),
5830             max_size=len(inputs),
5831     ))):
5832         if optional in ("optional", "empty"):
5833             inits[i]["optional"] = True
5834         if optional == "empty":
5835             empties.append(i)
5836     empties = set(empties)
5837     names = list(draw(sets(
5838         text_printable,
5839         min_size=len(inputs),
5840         max_size=len(inputs),
5841     )))
5842     schema = []
5843     for i, (klass, value, default) in enumerate(inputs):
5844         schema.append((names[i], klass(default=default, **inits[i])))
5845     seq_name = draw(text_letters())
5846     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5847     seq = Seq()
5848     expects = []
5849     for i, (klass, value, default) in enumerate(inputs):
5850         name = names[i]
5851         _, spec = schema[i]
5852         expect = {
5853             "name": name,
5854             "optional": False,
5855             "presented": False,
5856             "default_value": None if spec.default is None else default,
5857             "value": None,
5858         }
5859         if i in empties:
5860             expect["optional"] = True
5861         else:
5862             expect["presented"] = True
5863             expect["value"] = value
5864             if spec.optional:
5865                 expect["optional"] = True
5866             if default is not None and default == value:
5867                 expect["presented"] = False
5868             seq[name] = klass(value)
5869         expects.append(expect)
5870     return seq, expects
5871
5872
5873 @composite
5874 def sequences_strategy(draw, seq_klass):
5875     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
5876     inits = [
5877         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
5878         for tag, expled in zip(tags, draw(lists(
5879             booleans(),
5880             min_size=len(tags),
5881             max_size=len(tags),
5882         )))
5883     ]
5884     defaulted = set(
5885         i for i, is_default in enumerate(draw(lists(
5886             booleans(),
5887             min_size=len(tags),
5888             max_size=len(tags),
5889         ))) if is_default
5890     )
5891     names = list(draw(sets(
5892         text_printable,
5893         min_size=len(tags),
5894         max_size=len(tags),
5895     )))
5896     seq_expectses = draw(lists(
5897         sequence_strategy(seq_klass=seq_klass),
5898         min_size=len(tags),
5899         max_size=len(tags),
5900     ))
5901     seqs = [seq for seq, _ in seq_expectses]
5902     schema = []
5903     for i, (name, seq) in enumerate(zip(names, seqs)):
5904         schema.append((
5905             name,
5906             seq(default=(seq if i in defaulted else None), **inits[i]),
5907         ))
5908     seq_name = draw(text_letters())
5909     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
5910     seq_outer = Seq()
5911     expect_outers = []
5912     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
5913         expect = {
5914             "name": name,
5915             "expects": expects_inner,
5916             "presented": False,
5917         }
5918         seq_outer[name] = seq_inner
5919         if seq_outer.specs[name].default is None:
5920             expect["presented"] = True
5921         expect_outers.append(expect)
5922     return seq_outer, expect_outers
5923
5924
5925 class SeqMixing(object):
5926     def test_invalid_value_type(self):
5927         with self.assertRaises(InvalidValueType) as err:
5928             self.base_klass(123)
5929         repr(err.exception)
5930
5931     def test_invalid_value_type_set(self):
5932         class Seq(self.base_klass):
5933             schema = (("whatever", Boolean()),)
5934         seq = Seq()
5935         with self.assertRaises(InvalidValueType) as err:
5936             seq["whatever"] = Integer(123)
5937         repr(err.exception)
5938
5939     @given(booleans())
5940     def test_optional(self, optional):
5941         obj = self.base_klass(default=self.base_klass(), optional=optional)
5942         self.assertTrue(obj.optional)
5943
5944     @given(data_strategy())
5945     def test_ready(self, d):
5946         ready = {
5947             str(i): v for i, v in enumerate(d.draw(lists(
5948                 booleans(),
5949                 min_size=1,
5950                 max_size=3,
5951             )))
5952         }
5953         non_ready = {
5954             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
5955                 booleans(),
5956                 min_size=1,
5957                 max_size=3,
5958             )))
5959         }
5960         schema_input = []
5961         for name in d.draw(permutations(
5962                 list(ready.keys()) + list(non_ready.keys()),
5963         )):
5964             schema_input.append((name, Boolean()))
5965
5966         class Seq(self.base_klass):
5967             schema = tuple(schema_input)
5968         seq = Seq()
5969         for name in ready.keys():
5970             seq[name]
5971             seq[name] = Boolean()
5972         self.assertFalse(seq.ready)
5973         repr(seq)
5974         list(seq.pps())
5975         pprint(seq, big_blobs=True, with_decode_path=True)
5976         for name, value in ready.items():
5977             seq[name] = Boolean(value)
5978         self.assertFalse(seq.ready)
5979         repr(seq)
5980         list(seq.pps())
5981         pprint(seq, big_blobs=True, with_decode_path=True)
5982         with self.assertRaises(ObjNotReady) as err:
5983             seq.encode()
5984         repr(err.exception)
5985         with self.assertRaises(ObjNotReady) as err:
5986             encode2pass(seq)
5987         for name, value in non_ready.items():
5988             seq[name] = Boolean(value)
5989         self.assertTrue(seq.ready)
5990         repr(seq)
5991         list(seq.pps())
5992         pprint(seq, big_blobs=True, with_decode_path=True)
5993
5994     @given(data_strategy())
5995     def test_call(self, d):
5996         class SeqInherited(self.base_klass):
5997             pass
5998         for klass in (self.base_klass, SeqInherited):
5999             (
6000                 value_initial,
6001                 schema_initial,
6002                 impl_initial,
6003                 expl_initial,
6004                 default_initial,
6005                 optional_initial,
6006                 _decoded_initial,
6007             ) = d.draw(seq_values_strategy(seq_klass=klass))
6008             obj_initial = klass(
6009                 value_initial,
6010                 schema_initial,
6011                 impl_initial,
6012                 expl_initial,
6013                 default_initial,
6014                 optional_initial or False,
6015                 _decoded_initial,
6016             )
6017             (
6018                 value,
6019                 _,
6020                 impl,
6021                 expl,
6022                 default,
6023                 optional,
6024                 _decoded,
6025             ) = d.draw(seq_values_strategy(
6026                 seq_klass=klass,
6027                 do_expl=impl_initial is None,
6028             ))
6029             obj = obj_initial(value, impl, expl, default, optional)
6030             value_expected = default if value is None else value
6031             value_expected = (
6032                 default_initial if value_expected is None
6033                 else value_expected
6034             )
6035             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
6036             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6037             self.assertEqual(obj.expl_tag, expl or expl_initial)
6038             self.assertEqual(
6039                 {} if obj.default is None else obj.default._value,
6040                 getattr(default_initial if default is None else default, "_value", {}),
6041             )
6042             if obj.default is None:
6043                 optional = optional_initial if optional is None else optional
6044                 optional = False if optional is None else optional
6045             else:
6046                 optional = True
6047             self.assertEqual(list(obj.specs.items()), schema_initial or [])
6048             self.assertEqual(obj.optional, optional)
6049
6050     @given(data_strategy())
6051     def test_copy(self, d):
6052         class SeqInherited(self.base_klass):
6053             pass
6054         register_class(SeqInherited)
6055         for klass in (self.base_klass, SeqInherited):
6056             values = d.draw(seq_values_strategy(seq_klass=klass))
6057             obj = klass(*values)
6058             for copy_func in copy_funcs:
6059                 obj_copied = copy_func(obj)
6060                 self.assert_copied_basic_fields(obj, obj_copied)
6061                 self.assertEqual(obj.specs, obj_copied.specs)
6062                 self.assertEqual(obj._value, obj_copied._value)
6063
6064     @given(data_strategy())
6065     def test_stripped(self, d):
6066         value = d.draw(integers())
6067         tag_impl = tag_encode(d.draw(integers(min_value=1)))
6068
6069         class Seq(self.base_klass):
6070             impl = tag_impl
6071             schema = (("whatever", Integer()),)
6072         seq = Seq()
6073         seq["whatever"] = Integer(value)
6074         with self.assertRaises(NotEnoughData):
6075             seq.decode(seq.encode()[:-1])
6076
6077     @given(data_strategy())
6078     def test_stripped_expl(self, d):
6079         value = d.draw(integers())
6080         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
6081
6082         class Seq(self.base_klass):
6083             expl = tag_expl
6084             schema = (("whatever", Integer()),)
6085         seq = Seq()
6086         seq["whatever"] = Integer(value)
6087         with self.assertRaises(NotEnoughData):
6088             seq.decode(seq.encode()[:-1])
6089
6090     @given(integers(min_value=3), binary(min_size=2))
6091     def test_non_tag_mismatch_raised(self, junk_tag_num, junk):
6092         junk = tag_encode(junk_tag_num) + junk
6093         try:
6094             _, _, len_encoded = tag_strip(memoryview(junk))
6095             len_decode(len_encoded)
6096         except Exception:
6097             assume(True)
6098         else:
6099             assume(False)
6100
6101         class Seq(self.base_klass):
6102             schema = (
6103                 ("whatever", Integer()),
6104                 ("junk", Any()),
6105                 ("whenever", Integer()),
6106             )
6107         seq = Seq()
6108         seq["whatever"] = Integer(123)
6109         seq["junk"] = Any(junk)
6110         seq["whenever"] = Integer(123)
6111         with self.assertRaises(DecodeError):
6112             seq.decode(seq.encode())
6113
6114     @given(
6115         integers(min_value=31),
6116         integers(min_value=0),
6117         decode_path_strat,
6118     )
6119     def test_bad_tag(self, tag, offset, decode_path):
6120         with self.assertRaises(DecodeError) as err:
6121             self.base_klass().decode(
6122                 tag_encode(tag)[:-1],
6123                 offset=offset,
6124                 decode_path=decode_path,
6125             )
6126         repr(err.exception)
6127         self.assertEqual(err.exception.offset, offset)
6128         self.assertEqual(err.exception.decode_path, decode_path)
6129
6130     @given(
6131         integers(min_value=128),
6132         integers(min_value=0),
6133         decode_path_strat,
6134     )
6135     def test_bad_len(self, l, offset, decode_path):
6136         with self.assertRaises(DecodeError) as err:
6137             self.base_klass().decode(
6138                 self.base_klass.tag_default + len_encode(l)[:-1],
6139                 offset=offset,
6140                 decode_path=decode_path,
6141             )
6142         repr(err.exception)
6143         self.assertEqual(err.exception.offset, offset)
6144         self.assertEqual(err.exception.decode_path, decode_path)
6145
6146     def _assert_expects(self, seq, expects):
6147         for expect in expects:
6148             self.assertEqual(
6149                 seq.specs[expect["name"]].optional,
6150                 expect["optional"],
6151             )
6152             if expect["default_value"] is not None:
6153                 self.assertEqual(
6154                     seq.specs[expect["name"]].default,
6155                     expect["default_value"],
6156                 )
6157             if expect["presented"]:
6158                 self.assertIn(expect["name"], seq)
6159                 self.assertEqual(seq[expect["name"]], expect["value"])
6160
6161     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6162     @given(data_strategy())
6163     def test_symmetric(self, d):
6164         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
6165         tail_junk = d.draw(binary(max_size=5))
6166         decode_path = d.draw(decode_path_strat)
6167         self.assertTrue(seq.ready)
6168         self.assertFalse(seq.decoded)
6169         self._assert_expects(seq, expects)
6170         repr(seq)
6171         list(seq.pps())
6172         pprint(seq, big_blobs=True, with_decode_path=True)
6173         self.assertTrue(seq.ready)
6174         seq_encoded = seq.encode()
6175         self.assertEqual(encode2pass(seq), seq_encoded)
6176         seq_encoded_cer = encode_cer(seq)
6177         self.assertNotEqual(seq_encoded_cer, seq_encoded)
6178         self.assertSequenceEqual(
6179             seq.decod(seq_encoded_cer, ctx={"bered": True}).encode(),
6180             seq_encoded,
6181         )
6182         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
6183         self.assertFalse(seq_decoded.lenindef)
6184         self.assertFalse(seq_decoded.ber_encoded)
6185         self.assertFalse(seq_decoded.bered)
6186
6187         t, _, lv = tag_strip(seq_encoded)
6188         _, _, v = len_decode(lv)
6189         seq_encoded_lenindef = t + LENINDEF + v + EOC
6190         with self.assertRaises(DecodeError):
6191             seq.decode(seq_encoded_lenindef)
6192         ctx_copied = deepcopy(ctx_dummy)
6193         ctx_copied["bered"] = True
6194         seq_decoded_lenindef, tail_lenindef = seq.decode(
6195             seq_encoded_lenindef + tail_junk,
6196             ctx=ctx_copied,
6197         )
6198         del ctx_copied["bered"]
6199         self.assertDictEqual(ctx_copied, ctx_dummy)
6200         self.assertTrue(seq_decoded_lenindef.lenindef)
6201         self.assertTrue(seq_decoded_lenindef.bered)
6202         seq_decoded_lenindef = copy(seq_decoded_lenindef)
6203         self.assertTrue(seq_decoded_lenindef.lenindef)
6204         self.assertTrue(seq_decoded_lenindef.bered)
6205         with self.assertRaises(DecodeError):
6206             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
6207         with self.assertRaises(DecodeError):
6208             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
6209         repr(seq_decoded_lenindef)
6210         list(seq_decoded_lenindef.pps())
6211         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
6212         self.assertTrue(seq_decoded_lenindef.ready)
6213
6214         for decoded, decoded_tail, encoded in (
6215                 (seq_decoded, tail, seq_encoded),
6216                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
6217         ):
6218             self.assertEqual(decoded_tail, tail_junk)
6219             self._assert_expects(decoded, expects)
6220             self.assertEqual(seq, decoded)
6221             self.assertEqual(decoded.encode(), seq_encoded)
6222             self.assertEqual(decoded.tlvlen, len(encoded))
6223             for expect in expects:
6224                 if not expect["presented"]:
6225                     self.assertNotIn(expect["name"], decoded)
6226                     continue
6227                 self.assertIn(expect["name"], decoded)
6228                 obj = decoded[expect["name"]]
6229                 self.assertTrue(obj.decoded)
6230                 offset = obj.expl_offset if obj.expled else obj.offset
6231                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6232                 self.assertSequenceEqual(
6233                     seq_encoded[offset:offset + tlvlen],
6234                     obj.encode(),
6235                 )
6236
6237             evgens = list(seq.decode_evgen(
6238                 encoded + decoded_tail,
6239                 decode_path=decode_path,
6240                 ctx={"bered": True},
6241             ))
6242             self.assertEqual(len(evgens), len(list(decoded._values_for_encoding())) + 1)
6243             for _decode_path, obj, _ in evgens[:-1]:
6244                 self.assertEqual(_decode_path[:-1], decode_path)
6245                 repr(obj)
6246                 list(obj.pps())
6247             _decode_path, obj, tail = evgens[-1]
6248             self.assertEqual(_decode_path, decode_path)
6249             repr(obj)
6250             list(obj.pps())
6251
6252         assert_exceeding_data(
6253             self,
6254             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
6255             tail_junk,
6256         )
6257
6258     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6259     @given(data_strategy())
6260     def test_symmetric_with_seq(self, d):
6261         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
6262         self.assertTrue(seq.ready)
6263         seq_encoded = seq.encode()
6264         self.assertEqual(encode2pass(seq), seq_encoded)
6265         seq_decoded, tail = seq.decode(seq_encoded)
6266         self.assertEqual(tail, b"")
6267         self.assertTrue(seq.ready)
6268         self.assertEqual(seq, seq_decoded)
6269         self.assertEqual(seq_decoded.encode(), seq_encoded)
6270         for expect_outer in expect_outers:
6271             if not expect_outer["presented"]:
6272                 self.assertNotIn(expect_outer["name"], seq_decoded)
6273                 continue
6274             self.assertIn(expect_outer["name"], seq_decoded)
6275             obj = seq_decoded[expect_outer["name"]]
6276             self.assertTrue(obj.decoded)
6277             offset = obj.expl_offset if obj.expled else obj.offset
6278             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
6279             self.assertSequenceEqual(
6280                 seq_encoded[offset:offset + tlvlen],
6281                 obj.encode(),
6282             )
6283             self._assert_expects(obj, expect_outer["expects"])
6284
6285     @given(data_strategy())
6286     def test_default_disappears(self, d):
6287         _schema = list(d.draw(dictionaries(
6288             text_letters(),
6289             sets(integers(), min_size=2, max_size=2),
6290             min_size=1,
6291         )).items())
6292
6293         class Seq(self.base_klass):
6294             schema = [
6295                 (n, Integer(default=d))
6296                 for n, (_, d) in _schema
6297             ]
6298         seq = Seq()
6299         for name, (value, _) in _schema:
6300             seq[name] = Integer(value)
6301         self.assertEqual(len(seq._value), len(_schema))
6302         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
6303         self.assertGreater(len(seq.encode()), len(empty_seq))
6304         for name, (_, default) in _schema:
6305             seq[name] = Integer(default)
6306         self.assertEqual(len(seq._value), 0)
6307         self.assertSequenceEqual(seq.encode(), empty_seq)
6308
6309     @given(data_strategy())
6310     def test_encoded_default_not_accepted(self, d):
6311         _schema = list(d.draw(dictionaries(
6312             text_letters(),
6313             integers(),
6314             min_size=1,
6315         )).items())
6316         tags = [tag_encode(tag) for tag in d.draw(sets(
6317             integers(min_value=1),
6318             min_size=len(_schema),
6319             max_size=len(_schema),
6320         ))]
6321
6322         class Wahl(Choice):
6323             schema = (("int", Integer()),)
6324
6325         class SeqWithoutDefault(self.base_klass):
6326             schema = [
6327                 (n, Wahl(expl=t))
6328                 for (n, _), t in zip(_schema, tags)
6329             ]
6330         seq_without_default = SeqWithoutDefault()
6331         for name, value in _schema:
6332             seq_without_default[name] = Wahl(("int", Integer(value)))
6333         seq_encoded = seq_without_default.encode()
6334         seq_without_default.decode(seq_encoded)
6335         self.assertEqual(
6336             len(list(seq_without_default.decode_evgen(seq_encoded))),
6337             len(_schema) * 2 + 1,
6338         )
6339
6340         class SeqWithDefault(self.base_klass):
6341             schema = [
6342                 (n, Wahl(default=Wahl(("int", Integer(v))), expl=t))
6343                 for (n, v), t in zip(_schema, tags)
6344             ]
6345         seq_with_default = SeqWithDefault()
6346         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6347             seq_with_default.decode(seq_encoded)
6348         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6349             list(seq_with_default.decode_evgen(seq_encoded))
6350         for ctx in ({"bered": True}, {"allow_default_values": True}):
6351             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
6352             self.assertTrue(seq_decoded.ber_encoded)
6353             self.assertTrue(seq_decoded.bered)
6354             seq_decoded = copy(seq_decoded)
6355             self.assertTrue(seq_decoded.ber_encoded)
6356             self.assertTrue(seq_decoded.bered)
6357             for name, value in _schema:
6358                 self.assertEqual(seq_decoded[name], seq_with_default[name])
6359                 self.assertEqual(seq_decoded[name].value, value)
6360             self.assertEqual(
6361                 len(list(seq_with_default.decode_evgen(seq_encoded, ctx=ctx))),
6362                 len(_schema) + 1,
6363             )
6364
6365         seq_without_default = SeqWithoutDefault()
6366         for name, value in _schema:
6367             seq_without_default[name] = Wahl(("int", Integer(value + 1)))
6368         seq_encoded = seq_without_default.encode()
6369         seq_with_default.decode(seq_encoded)
6370         self.assertEqual(
6371             len(list(seq_with_default.decode_evgen(seq_encoded))),
6372             len(_schema) + 1,
6373         )
6374
6375     @given(data_strategy())
6376     def test_missing_from_spec(self, d):
6377         names = list(d.draw(sets(text_letters(), min_size=2)))
6378         tags = [tag_encode(tag) for tag in d.draw(sets(
6379             integers(min_value=1),
6380             min_size=len(names),
6381             max_size=len(names),
6382         ))]
6383         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
6384
6385         class SeqFull(self.base_klass):
6386             schema = [(n, Integer(impl=t)) for n, t in names_tags]
6387         seq_full = SeqFull()
6388         for i, name in enumerate(names):
6389             seq_full[name] = Integer(i)
6390         seq_encoded = seq_full.encode()
6391         altered = names_tags[:-2] + names_tags[-1:]
6392
6393         class SeqMissing(self.base_klass):
6394             schema = [(n, Integer(impl=t)) for n, t in altered]
6395         seq_missing = SeqMissing()
6396         with self.assertRaises(TagMismatch):
6397             seq_missing.decode(seq_encoded)
6398         with self.assertRaises(TagMismatch):
6399             list(seq_missing.decode_evgen(seq_encoded))
6400
6401     def test_bered(self):
6402         class Seq(self.base_klass):
6403             schema = (("underlying", Boolean()),)
6404         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
6405         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6406         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6407         self.assertFalse(decoded.ber_encoded)
6408         self.assertFalse(decoded.lenindef)
6409         self.assertTrue(decoded.bered)
6410         decoded = copy(decoded)
6411         self.assertFalse(decoded.ber_encoded)
6412         self.assertFalse(decoded.lenindef)
6413         self.assertTrue(decoded.bered)
6414
6415         class Seq(self.base_klass):
6416             schema = (("underlying", OctetString()),)
6417         encoded = (
6418             tag_encode(form=TagFormConstructed, num=4) +
6419             LENINDEF +
6420             OctetString(b"whatever").encode() +
6421             EOC
6422         )
6423         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6424         with self.assertRaises(DecodeError):
6425             Seq().decode(encoded)
6426         with self.assertRaises(DecodeError):
6427             list(Seq().decode_evgen(encoded))
6428         list(Seq().decode_evgen(encoded, ctx={"bered": True}))
6429         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
6430         self.assertFalse(decoded.ber_encoded)
6431         self.assertFalse(decoded.lenindef)
6432         self.assertTrue(decoded.bered)
6433         decoded = copy(decoded)
6434         self.assertFalse(decoded.ber_encoded)
6435         self.assertFalse(decoded.lenindef)
6436         self.assertTrue(decoded.bered)
6437
6438
6439 class TestSequence(SeqMixing, CommonMixin, TestCase):
6440     base_klass = Sequence
6441
6442     @given(
6443         integers(),
6444         binary(min_size=1),
6445     )
6446     def test_remaining(self, value, junk):
6447         class Seq(Sequence):
6448             schema = (
6449                 ("whatever", Integer()),
6450             )
6451         int_encoded = Integer(value).encode()
6452         junked = b"".join((
6453             Sequence.tag_default,
6454             len_encode(len(int_encoded + junk)),
6455             int_encoded + junk,
6456         ))
6457         with assertRaisesRegex(self, DecodeError, "remaining"):
6458             Seq().decode(junked)
6459
6460     @given(sets(text_letters(), min_size=2))
6461     def test_obj_unknown(self, names):
6462         missing = names.pop()
6463
6464         class Seq(Sequence):
6465             schema = [(n, Boolean()) for n in names]
6466         seq = Seq()
6467         with self.assertRaises(ObjUnknown) as err:
6468             seq[missing]
6469         repr(err.exception)
6470         with self.assertRaises(ObjUnknown) as err:
6471             seq[missing] = Boolean()
6472         repr(err.exception)
6473
6474     def test_x690_vector(self):
6475         class Seq(Sequence):
6476             schema = (
6477                 ("name", IA5String()),
6478                 ("ok", Boolean()),
6479             )
6480         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
6481         self.assertEqual(seq["name"], "Smith")
6482         self.assertEqual(seq["ok"], True)
6483
6484
6485 class TestSet(SeqMixing, CommonMixin, TestCase):
6486     base_klass = Set
6487
6488     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6489     @given(data_strategy())
6490     def test_sorted(self, d):
6491         class DummySeq(Sequence):
6492             schema = (("null", Null()),)
6493
6494         tag_nums = d.draw(sets(integers(min_value=1), min_size=1, max_size=50))
6495         _, _, dummy_seq_tag_num = tag_decode(DummySeq.tag_default)
6496         assume(any(i > dummy_seq_tag_num for i in tag_nums))
6497         tag_nums -= set([dummy_seq_tag_num])
6498         _schema = [(str(i), OctetString(impl=tag_encode(i))) for i in tag_nums]
6499         _schema.append(("seq", DummySeq()))
6500
6501         class Seq(Set):
6502             schema = d.draw(permutations(_schema))
6503         seq = Seq()
6504         for name, _ in _schema:
6505             if name != "seq":
6506                 seq[name] = OctetString(name.encode("ascii"))
6507         seq["seq"] = DummySeq((("null", Null()),))
6508
6509         seq_encoded = seq.encode()
6510         seq_decoded, _ = seq.decode(seq_encoded)
6511         seq_encoded_expected = []
6512         for tag_num in sorted(tag_nums | set([dummy_seq_tag_num])):
6513             if tag_num == dummy_seq_tag_num:
6514                 seq_encoded_expected.append(seq["seq"].encode())
6515             else:
6516                 seq_encoded_expected.append(seq[str(tag_num)].encode())
6517         self.assertSequenceEqual(
6518             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6519             b"".join(seq_encoded_expected),
6520         )
6521
6522         encoded = b"".join(seq[str(i)].encode() for i in tag_nums)
6523         encoded += seq["seq"].encode()
6524         seq_encoded = b"".join((
6525             Set.tag_default,
6526             len_encode(len(encoded)),
6527             encoded,
6528         ))
6529         with assertRaisesRegex(self, DecodeError, "unordered SET"):
6530             seq.decode(seq_encoded)
6531         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6532             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6533             self.assertTrue(seq_decoded.ber_encoded)
6534             self.assertTrue(seq_decoded.bered)
6535             seq_decoded = copy(seq_decoded)
6536             self.assertTrue(seq_decoded.ber_encoded)
6537             self.assertTrue(seq_decoded.bered)
6538
6539     def test_same_value_twice(self):
6540         class Seq(Set):
6541             schema = (
6542                 ("bool", Boolean()),
6543                 ("int", Integer()),
6544             )
6545
6546         encoded = b"".join((
6547             Integer(123).encode(),
6548             Integer(234).encode(),
6549             Boolean(True).encode(),
6550         ))
6551         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
6552         with self.assertRaises(TagMismatch):
6553             Seq().decod(encoded, ctx={"allow_unordered_set": True})
6554
6555
6556 @composite
6557 def seqof_values_strategy(draw, schema=None, do_expl=False):
6558     if schema is None:
6559         schema = draw(sampled_from((Boolean(), Integer())))
6560     bound_min, bound_max = sorted(draw(sets(
6561         integers(min_value=0, max_value=10),
6562         min_size=2,
6563         max_size=2,
6564     )))
6565     if isinstance(schema, Boolean):
6566         values_generator = booleans().map(Boolean)
6567     elif isinstance(schema, Integer):
6568         values_generator = integers().map(Integer)
6569     values_generator = lists(
6570         values_generator,
6571         min_size=bound_min,
6572         max_size=bound_max,
6573     )
6574     values = draw(one_of(none(), values_generator))
6575     impl = None
6576     expl = None
6577     if do_expl:
6578         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6579     else:
6580         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6581     default = draw(one_of(none(), values_generator))
6582     optional = draw(one_of(none(), booleans()))
6583     _decoded = (
6584         draw(integers(min_value=0)),
6585         draw(integers(min_value=0)),
6586         draw(integers(min_value=0)),
6587     )
6588     return (
6589         schema,
6590         values,
6591         (bound_min, bound_max),
6592         impl,
6593         expl,
6594         default,
6595         optional,
6596         _decoded,
6597     )
6598
6599
6600 class SeqOfMixing(object):
6601     def test_invalid_value_type(self):
6602         with self.assertRaises(InvalidValueType) as err:
6603             self.base_klass(123)
6604         repr(err.exception)
6605
6606     def test_invalid_values_type(self):
6607         class SeqOf(self.base_klass):
6608             schema = Integer()
6609         with self.assertRaises(InvalidValueType) as err:
6610             SeqOf([Integer(123), Boolean(False), Integer(234)])
6611         repr(err.exception)
6612
6613     def test_schema_required(self):
6614         with assertRaisesRegex(self, ValueError, "schema must be specified"):
6615             self.base_klass.__mro__[1]()
6616
6617     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
6618     def test_comparison(self, value1, value2, tag1, tag2):
6619         class SeqOf(self.base_klass):
6620             schema = Boolean()
6621         obj1 = SeqOf([Boolean(value1)])
6622         obj2 = SeqOf([Boolean(value2)])
6623         self.assertEqual(obj1 == obj2, value1 == value2)
6624         self.assertEqual(obj1 != obj2, value1 != value2)
6625         self.assertEqual(obj1 == list(obj2), value1 == value2)
6626         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
6627         obj1 = SeqOf([Boolean(value1)], impl=tag1)
6628         obj2 = SeqOf([Boolean(value1)], impl=tag2)
6629         self.assertEqual(obj1 == obj2, tag1 == tag2)
6630         self.assertEqual(obj1 != obj2, tag1 != tag2)
6631
6632     @given(lists(booleans()))
6633     def test_iter(self, values):
6634         class SeqOf(self.base_klass):
6635             schema = Boolean()
6636         obj = SeqOf([Boolean(value) for value in values])
6637         self.assertEqual(len(obj), len(values))
6638         for i, value in enumerate(obj):
6639             self.assertEqual(value, values[i])
6640
6641     @given(data_strategy())
6642     def test_ready(self, d):
6643         ready = [Integer(v) for v in d.draw(lists(
6644             integers(),
6645             min_size=1,
6646             max_size=3,
6647         ))]
6648         non_ready = [
6649             Integer() for _ in
6650             range(d.draw(integers(min_value=1, max_value=5)))
6651         ]
6652
6653         class SeqOf(self.base_klass):
6654             schema = Integer()
6655         values = d.draw(permutations(ready + non_ready))
6656         seqof = SeqOf()
6657         for value in values:
6658             seqof.append(value)
6659         self.assertFalse(seqof.ready)
6660         repr(seqof)
6661         list(seqof.pps())
6662         pprint(seqof, big_blobs=True, with_decode_path=True)
6663         with self.assertRaises(ObjNotReady) as err:
6664             seqof.encode()
6665         repr(err.exception)
6666         with self.assertRaises(ObjNotReady) as err:
6667             encode2pass(seqof)
6668         for i, value in enumerate(values):
6669             self.assertEqual(seqof[i], value)
6670             if not seqof[i].ready:
6671                 seqof[i] = Integer(i)
6672         self.assertTrue(seqof.ready)
6673         repr(seqof)
6674         list(seqof.pps())
6675         pprint(seqof, big_blobs=True, with_decode_path=True)
6676
6677     def test_spec_mismatch(self):
6678         class SeqOf(self.base_klass):
6679             schema = Integer()
6680         seqof = SeqOf()
6681         seqof.append(Integer(123))
6682         with self.assertRaises(ValueError):
6683             seqof.append(Boolean(False))
6684         with self.assertRaises(ValueError):
6685             seqof[0] = Boolean(False)
6686
6687     @given(data_strategy())
6688     def test_bounds_satisfied(self, d):
6689         class SeqOf(self.base_klass):
6690             schema = Boolean()
6691         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
6692         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6693         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
6694         SeqOf(value=value, bounds=(bound_min, bound_max))
6695
6696     @given(data_strategy())
6697     def test_bounds_unsatisfied(self, d):
6698         class SeqOf(self.base_klass):
6699             schema = Boolean()
6700         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
6701         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6702         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
6703         with self.assertRaises(BoundsError) as err:
6704             SeqOf(value=value, bounds=(bound_min, bound_max))
6705         repr(err.exception)
6706         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6707             SeqOf(bounds=(bound_min, bound_max)).decode(
6708                 SeqOf(value).encode()
6709             )
6710         repr(err.exception)
6711         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6712             SeqOf(bounds=(bound_min, bound_max)).decode(
6713                 encode2pass(SeqOf(value))
6714             )
6715         value = [Boolean(True)] * d.draw(integers(
6716             min_value=bound_max + 1,
6717             max_value=bound_max + 10,
6718         ))
6719         with self.assertRaises(BoundsError) as err:
6720             SeqOf(value=value, bounds=(bound_min, bound_max))
6721         repr(err.exception)
6722         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6723             SeqOf(bounds=(bound_min, bound_max)).decode(
6724                 SeqOf(value).encode()
6725             )
6726         repr(err.exception)
6727         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6728             SeqOf(bounds=(bound_min, bound_max)).decode(
6729                 encode2pass(SeqOf(value))
6730             )
6731
6732     @given(integers(min_value=1, max_value=10))
6733     def test_out_of_bounds(self, bound_max):
6734         class SeqOf(self.base_klass):
6735             schema = Integer()
6736             bounds = (0, bound_max)
6737         seqof = SeqOf()
6738         for _ in range(bound_max):
6739             seqof.append(Integer(123))
6740         with self.assertRaises(BoundsError):
6741             seqof.append(Integer(123))
6742
6743     @given(data_strategy())
6744     def test_call(self, d):
6745         (
6746             schema_initial,
6747             value_initial,
6748             bounds_initial,
6749             impl_initial,
6750             expl_initial,
6751             default_initial,
6752             optional_initial,
6753             _decoded_initial,
6754         ) = d.draw(seqof_values_strategy())
6755
6756         class SeqOf(self.base_klass):
6757             schema = schema_initial
6758         obj_initial = SeqOf(
6759             value=value_initial,
6760             bounds=bounds_initial,
6761             impl=impl_initial,
6762             expl=expl_initial,
6763             default=default_initial,
6764             optional=optional_initial or False,
6765             _decoded=_decoded_initial,
6766         )
6767         (
6768             _,
6769             value,
6770             bounds,
6771             impl,
6772             expl,
6773             default,
6774             optional,
6775             _decoded,
6776         ) = d.draw(seqof_values_strategy(
6777             schema=schema_initial,
6778             do_expl=impl_initial is None,
6779         ))
6780         if (default is None) and (obj_initial.default is not None):
6781             bounds = None
6782         if (
6783                 (bounds is None) and
6784                 (value is not None) and
6785                 (bounds_initial is not None) and
6786                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
6787         ):
6788             value = None
6789         if (
6790                 (bounds is None) and
6791                 (default is not None) and
6792                 (bounds_initial is not None) and
6793                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
6794         ):
6795             default = None
6796         obj = obj_initial(
6797             value=value,
6798             bounds=bounds,
6799             impl=impl,
6800             expl=expl,
6801             default=default,
6802             optional=optional,
6803         )
6804         if obj.ready:
6805             value_expected = default if value is None else value
6806             value_expected = (
6807                 default_initial if value_expected is None
6808                 else value_expected
6809             )
6810             value_expected = () if value_expected is None else value_expected
6811             self.assertEqual(obj, value_expected)
6812         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6813         self.assertEqual(obj.expl_tag, expl or expl_initial)
6814         self.assertEqual(
6815             obj.default,
6816             default_initial if default is None else default,
6817         )
6818         if obj.default is None:
6819             optional = optional_initial if optional is None else optional
6820             optional = False if optional is None else optional
6821         else:
6822             optional = True
6823         self.assertEqual(obj.optional, optional)
6824         self.assertEqual(
6825             (obj._bound_min, obj._bound_max),
6826             bounds or bounds_initial or (0, float("+inf")),
6827         )
6828
6829     @given(seqof_values_strategy())
6830     def test_copy(self, values):
6831         _schema, value, bounds, impl, expl, default, optional, _decoded = values
6832
6833         class SeqOf(self.base_klass):
6834             schema = _schema
6835         register_class(SeqOf)
6836         obj = SeqOf(
6837             value=value,
6838             bounds=bounds,
6839             impl=impl,
6840             expl=expl,
6841             default=default,
6842             optional=optional or False,
6843             _decoded=_decoded,
6844         )
6845         for copy_func in copy_funcs:
6846             obj_copied = copy_func(obj)
6847             self.assert_copied_basic_fields(obj, obj_copied)
6848             self.assertEqual(obj._bound_min, obj_copied._bound_min)
6849             self.assertEqual(obj._bound_max, obj_copied._bound_max)
6850             self.assertEqual(obj._value, obj_copied._value)
6851
6852     @given(
6853         lists(binary()),
6854         integers(min_value=1).map(tag_encode),
6855     )
6856     def test_stripped(self, values, tag_impl):
6857         class SeqOf(self.base_klass):
6858             schema = OctetString()
6859         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
6860         with self.assertRaises(NotEnoughData):
6861             obj.decode(obj.encode()[:-1])
6862
6863     @given(
6864         lists(binary()),
6865         integers(min_value=1).map(tag_ctxc),
6866     )
6867     def test_stripped_expl(self, values, tag_expl):
6868         class SeqOf(self.base_klass):
6869             schema = OctetString()
6870         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
6871         with self.assertRaises(NotEnoughData):
6872             obj.decode(obj.encode()[:-1])
6873
6874     @given(
6875         integers(min_value=31),
6876         integers(min_value=0),
6877         decode_path_strat,
6878     )
6879     def test_bad_tag(self, tag, offset, decode_path):
6880         with self.assertRaises(DecodeError) as err:
6881             self.base_klass().decode(
6882                 tag_encode(tag)[:-1],
6883                 offset=offset,
6884                 decode_path=decode_path,
6885             )
6886         repr(err.exception)
6887         self.assertEqual(err.exception.offset, offset)
6888         self.assertEqual(err.exception.decode_path, decode_path)
6889
6890     @given(
6891         integers(min_value=128),
6892         integers(min_value=0),
6893         decode_path_strat,
6894     )
6895     def test_bad_len(self, l, offset, decode_path):
6896         with self.assertRaises(DecodeError) as err:
6897             self.base_klass().decode(
6898                 self.base_klass.tag_default + len_encode(l)[:-1],
6899                 offset=offset,
6900                 decode_path=decode_path,
6901             )
6902         repr(err.exception)
6903         self.assertEqual(err.exception.offset, offset)
6904         self.assertEqual(err.exception.decode_path, decode_path)
6905
6906     @given(binary(min_size=1))
6907     def test_tag_mismatch(self, impl):
6908         assume(impl != self.base_klass.tag_default)
6909         with self.assertRaises(TagMismatch):
6910             self.base_klass(impl=impl).decode(self.base_klass().encode())
6911
6912     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6913     @given(
6914         seqof_values_strategy(schema=Integer()),
6915         lists(integers().map(Integer)),
6916         integers(min_value=1).map(tag_ctxc),
6917         integers(min_value=0),
6918         binary(max_size=5),
6919         decode_path_strat,
6920     )
6921     def test_symmetric(self, values, value, tag_expl, offset, tail_junk, decode_path):
6922         _, _, _, _, _, default, optional, _decoded = values
6923
6924         class SeqOf(self.base_klass):
6925             schema = Integer()
6926         obj = SeqOf(
6927             value=value,
6928             default=default,
6929             optional=optional,
6930             _decoded=_decoded,
6931         )
6932         repr(obj)
6933         list(obj.pps())
6934         pprint(obj, big_blobs=True, with_decode_path=True)
6935         self.assertFalse(obj.expled)
6936         obj_encoded = obj.encode()
6937         self.assertEqual(encode2pass(obj), obj_encoded)
6938         obj_encoded_cer = encode_cer(obj)
6939         self.assertNotEqual(obj_encoded_cer, obj_encoded)
6940         self.assertSequenceEqual(
6941             obj.decod(obj_encoded_cer, ctx={"bered": True}).encode(),
6942             obj_encoded,
6943         )
6944         obj_expled = obj(value, expl=tag_expl)
6945         self.assertTrue(obj_expled.expled)
6946         repr(obj_expled)
6947         list(obj_expled.pps())
6948         pprint(obj_expled, big_blobs=True, with_decode_path=True)
6949         obj_expled_encoded = obj_expled.encode()
6950         ctx_copied = deepcopy(ctx_dummy)
6951         obj_decoded, tail = obj_expled.decode(
6952             obj_expled_encoded + tail_junk,
6953             offset=offset,
6954             ctx=ctx_copied,
6955         )
6956         self.assertDictEqual(ctx_copied, ctx_dummy)
6957         repr(obj_decoded)
6958         list(obj_decoded.pps())
6959         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
6960         self.assertEqual(tail, tail_junk)
6961         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
6962         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
6963         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
6964         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
6965         self.assertEqual(
6966             obj_decoded.expl_llen,
6967             len(len_encode(len(obj_encoded))),
6968         )
6969         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
6970         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
6971         self.assertEqual(
6972             obj_decoded.offset,
6973             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
6974         )
6975         self.assertEqual(obj_decoded.expl_offset, offset)
6976         for obj_inner in obj_decoded:
6977             self.assertIn(obj_inner, obj_decoded)
6978             self.assertSequenceEqual(
6979                 obj_inner.encode(),
6980                 obj_expled_encoded[
6981                     obj_inner.offset - offset:
6982                     obj_inner.offset + obj_inner.tlvlen - offset
6983                 ],
6984             )
6985
6986         t, _, lv = tag_strip(obj_encoded)
6987         _, _, v = len_decode(lv)
6988         obj_encoded_lenindef = t + LENINDEF + v + EOC
6989         with self.assertRaises(DecodeError):
6990             obj.decode(obj_encoded_lenindef)
6991         obj_decoded_lenindef, tail_lenindef = obj.decode(
6992             obj_encoded_lenindef + tail_junk,
6993             ctx={"bered": True},
6994         )
6995         self.assertTrue(obj_decoded_lenindef.lenindef)
6996         self.assertTrue(obj_decoded_lenindef.bered)
6997         obj_decoded_lenindef = copy(obj_decoded_lenindef)
6998         self.assertTrue(obj_decoded_lenindef.lenindef)
6999         self.assertTrue(obj_decoded_lenindef.bered)
7000         repr(obj_decoded_lenindef)
7001         list(obj_decoded_lenindef.pps())
7002         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
7003         self.assertEqual(tail_lenindef, tail_junk)
7004         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
7005         with self.assertRaises(DecodeError):
7006             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
7007         with self.assertRaises(DecodeError):
7008             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
7009
7010         evgens = list(obj.decode_evgen(
7011             obj_encoded_lenindef + tail_junk,
7012             decode_path=decode_path,
7013             ctx={"bered": True},
7014         ))
7015         self.assertEqual(len(evgens), len(obj_decoded_lenindef) + 1)
7016         for i, (_decode_path, obj, _) in enumerate(evgens[:-1]):
7017             self.assertEqual(_decode_path, decode_path + (str(i),))
7018             repr(obj)
7019             list(obj.pps())
7020         _decode_path, obj, tail = evgens[-1]
7021         self.assertEqual(_decode_path, decode_path)
7022         repr(obj)
7023         list(obj.pps())
7024
7025         assert_exceeding_data(
7026             self,
7027             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
7028             tail_junk,
7029         )
7030
7031     def test_bered(self):
7032         class SeqOf(self.base_klass):
7033             schema = Boolean()
7034         encoded = Boolean(False).encode()
7035         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
7036         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
7037         with self.assertRaises(DecodeError):
7038             SeqOf().decode(encoded)
7039         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
7040         self.assertFalse(decoded.ber_encoded)
7041         self.assertFalse(decoded.lenindef)
7042         self.assertTrue(decoded.bered)
7043         decoded = copy(decoded)
7044         self.assertFalse(decoded.ber_encoded)
7045         self.assertFalse(decoded.lenindef)
7046         self.assertTrue(decoded.bered)
7047
7048         class SeqOf(self.base_klass):
7049             schema = OctetString()
7050         encoded = OctetString(b"whatever").encode()
7051         encoded += (
7052             tag_encode(form=TagFormConstructed, num=4) +
7053             LENINDEF +
7054             OctetString(b"whatever").encode() +
7055             EOC
7056         )
7057         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
7058         with self.assertRaises(DecodeError):
7059             SeqOf().decode(encoded)
7060         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
7061         self.assertFalse(decoded.ber_encoded)
7062         self.assertFalse(decoded.lenindef)
7063         self.assertTrue(decoded.bered)
7064         decoded = copy(decoded)
7065         self.assertFalse(decoded.ber_encoded)
7066         self.assertFalse(decoded.lenindef)
7067         self.assertTrue(decoded.bered)
7068
7069
7070 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
7071     class SeqOf(SequenceOf):
7072         schema = "whatever"
7073     base_klass = SeqOf
7074
7075     def _test_symmetric_compare_objs(self, obj1, obj2):
7076         self.assertEqual(obj1, obj2)
7077         self.assertSequenceEqual(list(obj1), list(obj2))
7078
7079     def test_iterator_pickling(self):
7080         class SeqOf(SequenceOf):
7081             schema = Integer()
7082         register_class(SeqOf)
7083         seqof = SeqOf()
7084         pickle_dumps(seqof)
7085         seqof = seqof(iter(six_xrange(10)))
7086         with assertRaisesRegex(self, ValueError, "iterator"):
7087             pickle_dumps(seqof)
7088
7089     def test_iterator_bounds(self):
7090         class SeqOf(SequenceOf):
7091             schema = Integer()
7092             bounds = (10, 20)
7093         seqof = None
7094
7095         def gen(n):
7096             for i in six_xrange(n):
7097                 yield Integer(i)
7098         for n in (9, 21):
7099             seqof = SeqOf(gen(n))
7100             self.assertTrue(seqof.ready)
7101             with self.assertRaises(BoundsError):
7102                 seqof.encode()
7103             self.assertFalse(seqof.ready)
7104             seqof = seqof(gen(n))
7105             self.assertTrue(seqof.ready)
7106             with self.assertRaises(BoundsError):
7107                 encode_cer(seqof)
7108             self.assertFalse(seqof.ready)
7109
7110     def test_iterator_twice(self):
7111         class SeqOf(SequenceOf):
7112             schema = Integer()
7113             bounds = (1, float("+inf"))
7114
7115         def gen():
7116             for i in six_xrange(10):
7117                 yield Integer(i)
7118         seqof = SeqOf(gen())
7119         self.assertTrue(seqof.ready)
7120         seqof.encode()
7121         self.assertFalse(seqof.ready)
7122         register_class(SeqOf)
7123         pickle_dumps(seqof)
7124
7125     def test_iterator_2pass(self):
7126         class SeqOf(SequenceOf):
7127             schema = Integer()
7128             bounds = (1, float("+inf"))
7129
7130         def gen():
7131             for i in six_xrange(10):
7132                 yield Integer(i)
7133         seqof = SeqOf(gen())
7134         self.assertTrue(seqof.ready)
7135         _, state = seqof.encode1st()
7136         self.assertFalse(seqof.ready)
7137         seqof = seqof(gen())
7138         self.assertTrue(seqof.ready)
7139         buf = BytesIO()
7140         seqof.encode2nd(buf.write, iter(state))
7141         self.assertSequenceEqual(
7142             [int(i) for i in seqof.decod(buf.getvalue())],
7143             list(gen()),
7144         )
7145
7146     def test_non_ready_bound_min(self):
7147         class SeqOf(SequenceOf):
7148             schema = Integer()
7149             bounds = (1, float("+inf"))
7150         seqof = SeqOf()
7151         self.assertFalse(seqof.ready)
7152
7153
7154 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
7155     class SeqOf(SetOf):
7156         schema = "whatever"
7157     base_klass = SeqOf
7158
7159     def _test_symmetric_compare_objs(self, obj1, obj2):
7160         self.assertSetEqual(
7161             set(int(v) for v in obj1),
7162             set(int(v) for v in obj2),
7163         )
7164
7165     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
7166     @given(data_strategy())
7167     def test_sorted(self, d):
7168         values = [OctetString(v) for v in d.draw(lists(binary()))]
7169
7170         class Seq(SetOf):
7171             schema = OctetString()
7172         seq = Seq(values)
7173         seq_encoded = seq.encode()
7174         seq_decoded, _ = seq.decode(seq_encoded)
7175         self.assertSequenceEqual(
7176             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
7177             b"".join(sorted([v.encode() for v in values])),
7178         )
7179
7180     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
7181     @given(data_strategy())
7182     def test_unsorted(self, d):
7183         values = [OctetString(v).encode() for v in d.draw(sets(
7184             binary(min_size=1, max_size=5),
7185             min_size=2,
7186             max_size=5,
7187         ))]
7188         values = d.draw(permutations(values))
7189         assume(values != sorted(values))
7190         encoded = b"".join(values)
7191         seq_encoded = b"".join((
7192             SetOf.tag_default,
7193             len_encode(len(encoded)),
7194             encoded,
7195         ))
7196
7197         class Seq(SetOf):
7198             schema = OctetString()
7199         seq = Seq()
7200         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
7201             seq.decode(seq_encoded)
7202
7203         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
7204             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
7205             self.assertTrue(seq_decoded.ber_encoded)
7206             self.assertTrue(seq_decoded.bered)
7207             seq_decoded = copy(seq_decoded)
7208             self.assertTrue(seq_decoded.ber_encoded)
7209             self.assertTrue(seq_decoded.bered)
7210             self.assertSequenceEqual(
7211                 [obj.encode() for obj in seq_decoded],
7212                 values,
7213             )
7214
7215
7216 class TestGoMarshalVectors(TestCase):
7217     def runTest(self):
7218         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
7219         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
7220         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
7221         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
7222         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
7223
7224         class Seq(Sequence):
7225             schema = (
7226                 ("erste", Integer()),
7227                 ("zweite", Integer(optional=True))
7228             )
7229         seq = Seq()
7230         seq["erste"] = Integer(64)
7231         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7232         seq["erste"] = Integer(0x123456)
7233         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
7234         seq["erste"] = Integer(64)
7235         seq["zweite"] = Integer(65)
7236         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
7237
7238         class NestedSeq(Sequence):
7239             schema = (
7240                 ("nest", Seq()),
7241             )
7242         seq["erste"] = Integer(127)
7243         seq["zweite"] = None
7244         nested = NestedSeq()
7245         nested["nest"] = seq
7246         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
7247
7248         self.assertSequenceEqual(
7249             OctetString(b"\x01\x02\x03").encode(),
7250             hexdec("0403010203"),
7251         )
7252
7253         class Seq(Sequence):
7254             schema = (
7255                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
7256             )
7257         seq = Seq()
7258         seq["erste"] = Integer(64)
7259         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
7260
7261         class Seq(Sequence):
7262             schema = (
7263                 ("erste", Integer(expl=tag_ctxc(5))),
7264             )
7265         seq = Seq()
7266         seq["erste"] = Integer(64)
7267         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
7268
7269         class Seq(Sequence):
7270             schema = (
7271                 ("erste", Null(
7272                     impl=tag_encode(0, klass=TagClassContext),
7273                     optional=True,
7274                 )),
7275             )
7276         seq = Seq()
7277         seq["erste"] = Null()
7278         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
7279         seq["erste"] = None
7280         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7281
7282         self.assertSequenceEqual(
7283             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
7284             hexdec("170d3730303130313030303030305a"),
7285         )
7286         self.assertSequenceEqual(
7287             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
7288             hexdec("170d3039313131353232353631365a"),
7289         )
7290         self.assertSequenceEqual(
7291             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
7292             hexdec("180f32313030303430353132303130315a"),
7293         )
7294
7295         class Seq(Sequence):
7296             schema = (
7297                 ("erste", GeneralizedTime()),
7298             )
7299         seq = Seq()
7300         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
7301         self.assertSequenceEqual(
7302             seq.encode(),
7303             hexdec("3011180f32303039313131353232353631365a"),
7304         )
7305
7306         self.assertSequenceEqual(
7307             BitString((1, b"\x80")).encode(),
7308             hexdec("03020780"),
7309         )
7310         self.assertSequenceEqual(
7311             BitString((12, b"\x81\xF0")).encode(),
7312             hexdec("03030481f0"),
7313         )
7314
7315         self.assertSequenceEqual(
7316             ObjectIdentifier("1.2.3.4").encode(),
7317             hexdec("06032a0304"),
7318         )
7319         self.assertSequenceEqual(
7320             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
7321             hexdec("06092a864888932d010105"),
7322         )
7323         self.assertSequenceEqual(
7324             ObjectIdentifier("2.100.3").encode(),
7325             hexdec("0603813403"),
7326         )
7327
7328         self.assertSequenceEqual(
7329             PrintableString("test").encode(),
7330             hexdec("130474657374"),
7331         )
7332         self.assertSequenceEqual(
7333             PrintableString("x" * 127).encode(),
7334             hexdec("137F" + "78" * 127),
7335         )
7336         self.assertSequenceEqual(
7337             PrintableString("x" * 128).encode(),
7338             hexdec("138180" + "78" * 128),
7339         )
7340         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
7341
7342         class Seq(Sequence):
7343             schema = (
7344                 ("erste", IA5String()),
7345             )
7346         seq = Seq()
7347         seq["erste"] = IA5String("test")
7348         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
7349
7350         class Seq(Sequence):
7351             schema = (
7352                 ("erste", PrintableString()),
7353             )
7354         seq = Seq()
7355         seq["erste"] = PrintableString("test")
7356         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
7357         # Asterisk is actually not allowable
7358         PrintableString._allowable_chars |= set(b"*")
7359         seq["erste"] = PrintableString("test*")
7360         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
7361         PrintableString._allowable_chars -= set(b"*")
7362
7363         class Seq(Sequence):
7364             schema = (
7365                 ("erste", Any(optional=True)),
7366                 ("zweite", Integer()),
7367             )
7368         seq = Seq()
7369         seq["zweite"] = Integer(64)
7370         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
7371
7372         class Seq(SetOf):
7373             schema = Integer()
7374         seq = Seq()
7375         seq.append(Integer(10))
7376         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
7377
7378         class _SeqOf(SequenceOf):
7379             schema = PrintableString()
7380
7381         class SeqOf(SequenceOf):
7382             schema = _SeqOf()
7383         _seqof = _SeqOf()
7384         _seqof.append(PrintableString("1"))
7385         seqof = SeqOf()
7386         seqof.append(_seqof)
7387         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
7388
7389         class Seq(Sequence):
7390             schema = (
7391                 ("erste", Integer(default=1)),
7392             )
7393         seq = Seq()
7394         seq["erste"] = Integer(0)
7395         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
7396         seq["erste"] = Integer(1)
7397         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
7398         seq["erste"] = Integer(2)
7399         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
7400
7401
7402 class TestPP(TestCase):
7403     @given(data_strategy())
7404     def test_oid_printing(self, d):
7405         oids = {
7406             str(ObjectIdentifier(k)): v * 2
7407             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
7408         }
7409         chosen = d.draw(sampled_from(sorted(oids)))
7410         chosen_id = oids[chosen]
7411         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
7412         self.assertNotIn(chosen_id, pp_console_row(pp))
7413         self.assertIn(
7414             chosen_id,
7415             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
7416         )
7417
7418
7419 class TestAutoAddSlots(TestCase):
7420     def runTest(self):
7421         class Inher(Integer):
7422             pass
7423
7424         with self.assertRaises(AttributeError):
7425             inher = Inher()
7426             inher.unexistent = "whatever"
7427
7428
7429 class TestOIDDefines(TestCase):
7430     @given(data_strategy())
7431     def runTest(self, d):
7432         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
7433         value_name_chosen = d.draw(sampled_from(value_names))
7434         oids = [
7435             ObjectIdentifier(oid)
7436             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
7437         ]
7438         oid_chosen = d.draw(sampled_from(oids))
7439         values = d.draw(lists(
7440             integers(),
7441             min_size=len(value_names),
7442             max_size=len(value_names),
7443         ))
7444         for definable_class in (Any, OctetString, BitString):
7445             _schema = [
7446                 ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
7447                     oid: Integer() for oid in oids[:-1]
7448                 }),))),
7449             ]
7450             for i, value_name in enumerate(value_names):
7451                 _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
7452
7453             class Seq(Sequence):
7454                 schema = _schema
7455             seq = Seq()
7456             for value_name, value in zip(value_names, values):
7457                 seq[value_name] = definable_class(Integer(value).encode())
7458             seq["type"] = oid_chosen
7459             seq, _ = Seq().decode(seq.encode())
7460             for value_name in value_names:
7461                 if value_name == value_name_chosen:
7462                     continue
7463                 self.assertIsNone(seq[value_name].defined)
7464             if value_name_chosen in oids[:-1]:
7465                 self.assertIsNotNone(seq[value_name_chosen].defined)
7466                 self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
7467                 self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
7468             repr(seq)
7469             list(seq.pps())
7470             pprint(seq, big_blobs=True, with_decode_path=True)
7471
7472
7473 class TestDefinesByPath(TestCase):
7474     def test_generated(self):
7475         class Seq(Sequence):
7476             schema = (
7477                 ("type", ObjectIdentifier()),
7478                 ("value", OctetString(expl=tag_ctxc(123))),
7479             )
7480
7481         class SeqInner(Sequence):
7482             schema = (
7483                 ("typeInner", ObjectIdentifier()),
7484                 ("valueInner", Any()),
7485             )
7486
7487         class PairValue(SetOf):
7488             schema = Any()
7489
7490         class Pair(Sequence):
7491             schema = (
7492                 ("type", ObjectIdentifier()),
7493                 ("value", PairValue()),
7494             )
7495
7496         class Pairs(SequenceOf):
7497             schema = Pair()
7498
7499         (
7500             type_integered,
7501             type_sequenced,
7502             type_innered,
7503             type_octet_stringed,
7504         ) = [
7505             ObjectIdentifier(oid)
7506             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
7507         ]
7508         seq_integered = Seq()
7509         seq_integered["type"] = type_integered
7510         seq_integered["value"] = OctetString(Integer(123).encode())
7511         seq_integered_raw = seq_integered.encode()
7512
7513         pairs = Pairs()
7514         pairs_input = (
7515             (type_octet_stringed, OctetString(b"whatever")),
7516             (type_integered, Integer(123)),
7517             (type_octet_stringed, OctetString(b"whenever")),
7518             (type_integered, Integer(234)),
7519         )
7520         for t, v in pairs_input:
7521             pairs.append(Pair((
7522                 ("type", t),
7523                 ("value", PairValue((Any(v),))),
7524             )))
7525         seq_inner = SeqInner()
7526         seq_inner["typeInner"] = type_innered
7527         seq_inner["valueInner"] = Any(pairs)
7528         seq_sequenced = Seq()
7529         seq_sequenced["type"] = type_sequenced
7530         seq_sequenced["value"] = OctetString(seq_inner.encode())
7531         seq_sequenced_raw = seq_sequenced.encode()
7532         repr(seq_sequenced)
7533         list(seq_sequenced.pps())
7534         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7535
7536         defines_by_path = []
7537         ctx_copied = deepcopy(ctx_dummy)
7538         seq_integered, _ = Seq().decode(
7539             seq_integered_raw,
7540             ctx=ctx_copied,
7541         )
7542         self.assertDictEqual(ctx_copied, ctx_dummy)
7543         self.assertIsNone(seq_integered["value"].defined)
7544         defines_by_path.append(
7545             (("type",), ((("value",), {
7546                 type_integered: Integer(),
7547                 type_sequenced: SeqInner(),
7548             }),))
7549         )
7550         ctx_copied["defines_by_path"] = defines_by_path
7551         seq_integered, _ = Seq().decode(
7552             seq_integered_raw,
7553             ctx=ctx_copied,
7554         )
7555         del ctx_copied["defines_by_path"]
7556         self.assertDictEqual(ctx_copied, ctx_dummy)
7557         self.assertIsNotNone(seq_integered["value"].defined)
7558         self.assertEqual(seq_integered["value"].defined[0], type_integered)
7559         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
7560         self.assertTrue(seq_integered_raw[
7561             seq_integered["value"].defined[1].offset:
7562         ].startswith(Integer(123).encode()))
7563         repr(seq_integered)
7564         list(seq_integered.pps())
7565         pprint(seq_integered, big_blobs=True, with_decode_path=True)
7566
7567         ctx_copied["defines_by_path"] = defines_by_path
7568         seq_sequenced, _ = Seq().decode(
7569             seq_sequenced_raw,
7570             ctx=ctx_copied,
7571         )
7572         del ctx_copied["defines_by_path"]
7573         self.assertDictEqual(ctx_copied, ctx_dummy)
7574         self.assertIsNotNone(seq_sequenced["value"].defined)
7575         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7576         seq_inner = seq_sequenced["value"].defined[1]
7577         self.assertIsNone(seq_inner["valueInner"].defined)
7578         repr(seq_sequenced)
7579         list(seq_sequenced.pps())
7580         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7581
7582         defines_by_path.append((
7583             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
7584             ((("valueInner",), {type_innered: Pairs()}),),
7585         ))
7586         ctx_copied["defines_by_path"] = defines_by_path
7587         seq_sequenced, _ = Seq().decode(
7588             seq_sequenced_raw,
7589             ctx=ctx_copied,
7590         )
7591         del ctx_copied["defines_by_path"]
7592         self.assertDictEqual(ctx_copied, ctx_dummy)
7593         self.assertIsNotNone(seq_sequenced["value"].defined)
7594         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7595         seq_inner = seq_sequenced["value"].defined[1]
7596         self.assertIsNotNone(seq_inner["valueInner"].defined)
7597         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7598         pairs = seq_inner["valueInner"].defined[1]
7599         for pair in pairs:
7600             self.assertIsNone(pair["value"][0].defined)
7601         repr(seq_sequenced)
7602         list(seq_sequenced.pps())
7603         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7604
7605         defines_by_path.append((
7606             (
7607                 "value",
7608                 DecodePathDefBy(type_sequenced),
7609                 "valueInner",
7610                 DecodePathDefBy(type_innered),
7611                 any,
7612                 "type",
7613             ),
7614             ((("value",), {
7615                 type_integered: Integer(),
7616                 type_octet_stringed: OctetString(),
7617             }),),
7618         ))
7619         ctx_copied["defines_by_path"] = defines_by_path
7620         seq_sequenced, _ = Seq().decode(
7621             seq_sequenced_raw,
7622             ctx=ctx_copied,
7623         )
7624         del ctx_copied["defines_by_path"]
7625         self.assertDictEqual(ctx_copied, ctx_dummy)
7626         self.assertIsNotNone(seq_sequenced["value"].defined)
7627         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7628         seq_inner = seq_sequenced["value"].defined[1]
7629         self.assertIsNotNone(seq_inner["valueInner"].defined)
7630         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7631         pairs_got = seq_inner["valueInner"].defined[1]
7632         for pair_input, pair_got in zip(pairs_input, pairs_got):
7633             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
7634             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
7635         repr(seq_sequenced)
7636         list(seq_sequenced.pps())
7637         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7638
7639     @given(oid_strategy(), integers())
7640     def test_simple(self, oid, tgt):
7641         class Inner(Sequence):
7642             schema = (
7643                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
7644                     ObjectIdentifier(oid): Integer(),
7645                 }),))),
7646             )
7647
7648         class Outer(Sequence):
7649             schema = (
7650                 ("inner", Inner()),
7651                 ("tgt", OctetString()),
7652             )
7653
7654         inner = Inner()
7655         inner["oid"] = ObjectIdentifier(oid)
7656         outer = Outer()
7657         outer["inner"] = inner
7658         outer["tgt"] = OctetString(Integer(tgt).encode())
7659         decoded, _ = Outer().decode(outer.encode())
7660         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
7661
7662     def test_remaining_data(self):
7663         oid = ObjectIdentifier("1.2.3")
7664
7665         class Seq(Sequence):
7666             schema = (
7667                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7668                     oid: Integer(),
7669                 }),))),
7670                 ("tgt", OctetString()),
7671             )
7672
7673         seq = Seq((
7674             ("oid", oid),
7675             ("tgt", OctetString(Integer(123).encode() + b"junk")),
7676         ))
7677         with assertRaisesRegex(self, DecodeError, "remaining data"):
7678             Seq().decode(seq.encode())
7679
7680     def test_remaining_data_seqof(self):
7681         oid = ObjectIdentifier("1.2.3")
7682
7683         class SeqOf(SetOf):
7684             schema = OctetString()
7685
7686         class Seq(Sequence):
7687             schema = (
7688                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7689                     oid: Integer(),
7690                 }),))),
7691                 ("tgt", SeqOf()),
7692             )
7693
7694         seq = Seq((
7695             ("oid", oid),
7696             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
7697         ))
7698         with assertRaisesRegex(self, DecodeError, "remaining data"):
7699             Seq().decode(seq.encode())
7700
7701
7702 class TestAbsDecodePath(TestCase):
7703     @given(
7704         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7705         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7706     )
7707     def test_concat(self, decode_path, rel_path):
7708         dp = abs_decode_path(decode_path, rel_path)
7709         self.assertSequenceEqual(dp, decode_path + rel_path)
7710         repr(dp)
7711
7712     @given(
7713         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7714         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7715     )
7716     def test_abs(self, decode_path, rel_path):
7717         self.assertSequenceEqual(
7718             abs_decode_path(decode_path, ("/",) + rel_path),
7719             rel_path,
7720         )
7721
7722     @given(
7723         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
7724         integers(min_value=1, max_value=3),
7725         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7726     )
7727     def test_dots(self, decode_path, number_of_dots, rel_path):
7728         self.assertSequenceEqual(
7729             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
7730             decode_path[:-number_of_dots] + rel_path,
7731         )
7732
7733
7734 class TestStrictDefaultExistence(TestCase):
7735     @given(data_strategy())
7736     def runTest(self, d):
7737         count = d.draw(integers(min_value=1, max_value=10))
7738         chosen = d.draw(integers(min_value=0, max_value=count - 1))
7739         _schema = [
7740             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
7741             for i in range(count)
7742         ]
7743         for klass in (Sequence, Set):
7744             class Seq(klass):
7745                 schema = _schema
7746             seq = Seq()
7747             for i in range(count):
7748                 seq["int%d" % i] = Integer(123)
7749             raw = seq.encode()
7750             chosen_choice = "int%d" % chosen
7751             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
7752             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
7753                 seq.decode(raw)
7754             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
7755             self.assertTrue(decoded.ber_encoded)
7756             self.assertTrue(decoded.bered)
7757             decoded = copy(decoded)
7758             self.assertTrue(decoded.ber_encoded)
7759             self.assertTrue(decoded.bered)
7760             decoded, _ = seq.decode(raw, ctx={"bered": True})
7761             self.assertTrue(decoded.ber_encoded)
7762             self.assertTrue(decoded.bered)
7763             decoded = copy(decoded)
7764             self.assertTrue(decoded.ber_encoded)
7765             self.assertTrue(decoded.bered)
7766
7767
7768 class TestX690PrefixedType(TestCase):
7769     def test_1(self):
7770         self.assertSequenceEqual(
7771             VisibleString("Jones").encode(),
7772             hexdec("1A054A6F6E6573"),
7773         )
7774
7775     def test_2(self):
7776         self.assertSequenceEqual(
7777             VisibleString(
7778                 "Jones",
7779                 impl=tag_encode(3, klass=TagClassApplication),
7780             ).encode(),
7781             hexdec("43054A6F6E6573"),
7782         )
7783
7784     def test_3(self):
7785         self.assertSequenceEqual(
7786             Any(
7787                 VisibleString(
7788                     "Jones",
7789                     impl=tag_encode(3, klass=TagClassApplication),
7790                 ),
7791                 expl=tag_ctxc(2),
7792             ).encode(),
7793             hexdec("A20743054A6F6E6573"),
7794         )
7795
7796     def test_4(self):
7797         self.assertSequenceEqual(
7798             OctetString(
7799                 VisibleString(
7800                     "Jones",
7801                     impl=tag_encode(3, klass=TagClassApplication),
7802                 ).encode(),
7803                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
7804             ).encode(),
7805             hexdec("670743054A6F6E6573"),
7806         )
7807
7808     def test_5(self):
7809         self.assertSequenceEqual(
7810             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
7811             hexdec("82054A6F6E6573"),
7812         )
7813
7814
7815 class TestExplOOB(TestCase):
7816     def runTest(self):
7817         expl = tag_ctxc(123)
7818         raw = Integer(123).encode() + Integer(234).encode()
7819         raw = b"".join((expl, len_encode(len(raw)), raw))
7820         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
7821             Integer(expl=expl).decode(raw)
7822         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
7823
7824
7825 class TestPickleDifferentVersion(TestCase):
7826     def runTest(self):
7827         pickled = pickle_dumps(Integer(123), pickle_proto)
7828         import pyderasn
7829         version_orig = pyderasn.__version__
7830         pyderasn.__version__ += "different"
7831         with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
7832             pickle_loads(pickled)
7833         pyderasn.__version__ = version_orig
7834         pickle_loads(pickled)
7835
7836
7837 class TestCERSetOrdering(TestCase):
7838     def test_vectors(self):
7839         """Taken from X.690-201508
7840         """
7841         class B(Choice):
7842             schema = (
7843                 ("c", Integer(impl=tag_ctxp(2))),
7844                 ("d", Integer(impl=tag_ctxp(4))),
7845             )
7846
7847         class F(Choice):
7848             schema = (
7849                 ("g", Integer(impl=tag_ctxp(5))),
7850                 ("h", Integer(impl=tag_ctxp(6))),
7851             )
7852
7853         class I(Choice):
7854             schema = (
7855                 ("j", Integer(impl=tag_ctxp(0))),
7856             )
7857
7858         class E(Choice):
7859             schema = (
7860                 ("f", F()),
7861                 ("i", I()),
7862             )
7863
7864         class A(Set):
7865             schema = (
7866                 ("a", Integer(impl=tag_ctxp(3))),
7867                 ("b", B(expl=tag_ctxc(1))),
7868                 ("e", E()),
7869             )
7870
7871         a = A((
7872             ("a", Integer(123)),
7873             ("b", B(("d", Integer(234)))),
7874             ("e", E(("f", F(("g", Integer(345)))))),
7875         ))
7876         order = sorted(a._values_for_encoding(), key=attrgetter("tag_order_cer"))
7877         self.assertSequenceEqual(
7878             [i.__class__.__name__ for i in order],
7879             ("E", "B", "Integer"),
7880         )