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