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