]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
010b17f7244dad285235ecfd7dbf88482efa0c5a
[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"
3544             obj = self.base_klass(s)
3545             for prop in kwargs.keys():
3546                 self.assertFalse(getattr(obj, prop))
3547             s += c
3548             with assertRaisesRegex(self, DecodeError, "non-printable"):
3549                 self.base_klass(s)
3550             self.base_klass(s, **kwargs)
3551             klass = self.base_klass(**kwargs)
3552             obj = klass(s)
3553             for prop in kwargs.keys():
3554                 self.assertTrue(getattr(obj, prop))
3555             obj = copy(obj)
3556             obj(s)
3557             for prop in kwargs.keys():
3558                 self.assertTrue(getattr(obj, prop))
3559
3560
3561 class TestTeletexString(
3562         UnicodeDecodeErrorMixin,
3563         StringMixin,
3564         CommonMixin,
3565         TestCase,
3566 ):
3567     base_klass = TeletexString
3568
3569
3570 class TestVideotexString(
3571         UnicodeDecodeErrorMixin,
3572         StringMixin,
3573         CommonMixin,
3574         TestCase,
3575 ):
3576     base_klass = VideotexString
3577
3578
3579 class TestIA5String(
3580         UnicodeDecodeErrorMixin,
3581         StringMixin,
3582         CommonMixin,
3583         TestCase,
3584 ):
3585     base_klass = IA5String
3586
3587
3588 class TestGraphicString(
3589         UnicodeDecodeErrorMixin,
3590         StringMixin,
3591         CommonMixin,
3592         TestCase,
3593 ):
3594     base_klass = GraphicString
3595
3596
3597 class TestVisibleString(
3598         UnicodeDecodeErrorMixin,
3599         StringMixin,
3600         CommonMixin,
3601         TestCase,
3602 ):
3603     base_klass = VisibleString
3604
3605     def test_x690_vector(self):
3606         obj, tail = VisibleString().decode(hexdec("1A054A6F6E6573"))
3607         self.assertSequenceEqual(tail, b"")
3608         self.assertEqual(str(obj), "Jones")
3609         self.assertFalse(obj.ber_encoded)
3610         self.assertFalse(obj.lenindef)
3611         self.assertFalse(obj.bered)
3612
3613         obj, tail = VisibleString().decode(
3614             hexdec("3A0904034A6F6E04026573"),
3615             ctx={"bered": True},
3616         )
3617         self.assertSequenceEqual(tail, b"")
3618         self.assertEqual(str(obj), "Jones")
3619         self.assertTrue(obj.ber_encoded)
3620         self.assertFalse(obj.lenindef)
3621         self.assertTrue(obj.bered)
3622         obj = copy(obj)
3623         self.assertTrue(obj.ber_encoded)
3624         self.assertFalse(obj.lenindef)
3625         self.assertTrue(obj.bered)
3626
3627         obj, tail = VisibleString().decode(
3628             hexdec("3A8004034A6F6E040265730000"),
3629             ctx={"bered": True},
3630         )
3631         self.assertSequenceEqual(tail, b"")
3632         self.assertEqual(str(obj), "Jones")
3633         self.assertTrue(obj.ber_encoded)
3634         self.assertTrue(obj.lenindef)
3635         self.assertTrue(obj.bered)
3636         obj = copy(obj)
3637         self.assertTrue(obj.ber_encoded)
3638         self.assertTrue(obj.lenindef)
3639         self.assertTrue(obj.bered)
3640
3641
3642 class TestGeneralString(
3643         UnicodeDecodeErrorMixin,
3644         StringMixin,
3645         CommonMixin,
3646         TestCase,
3647 ):
3648     base_klass = GeneralString
3649
3650
3651 class TestUniversalString(StringMixin, CommonMixin, TestCase):
3652     base_klass = UniversalString
3653
3654
3655 class TestBMPString(StringMixin, CommonMixin, TestCase):
3656     base_klass = BMPString
3657
3658
3659 @composite
3660 def generalized_time_values_strategy(
3661         draw,
3662         min_datetime,
3663         max_datetime,
3664         omit_ms=False,
3665         do_expl=False,
3666 ):
3667     value = None
3668     if draw(booleans()):
3669         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3670         if omit_ms:
3671             value = value.replace(microsecond=0)
3672     default = None
3673     if draw(booleans()):
3674         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
3675         if omit_ms:
3676             default = default.replace(microsecond=0)
3677     impl = None
3678     expl = None
3679     if do_expl:
3680         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3681     else:
3682         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3683     optional = draw(one_of(none(), booleans()))
3684     _decoded = (
3685         draw(integers(min_value=0)),
3686         draw(integers(min_value=0)),
3687         draw(integers(min_value=0)),
3688     )
3689     return (value, impl, expl, default, optional, _decoded)
3690
3691
3692 class TimeMixin(object):
3693     def test_invalid_value_type(self):
3694         with self.assertRaises(InvalidValueType) as err:
3695             self.base_klass(datetime.now().timetuple())
3696         repr(err.exception)
3697
3698     @given(data_strategy())
3699     def test_optional(self, d):
3700         default = d.draw(datetimes(
3701             min_value=self.min_datetime,
3702             max_value=self.max_datetime,
3703         ))
3704         optional = d.draw(booleans())
3705         obj = self.base_klass(default=default, optional=optional)
3706         self.assertTrue(obj.optional)
3707
3708     @given(data_strategy())
3709     def test_ready(self, d):
3710         obj = self.base_klass()
3711         self.assertFalse(obj.ready)
3712         repr(obj)
3713         list(obj.pps())
3714         pprint(obj, big_blobs=True, with_decode_path=True)
3715         with self.assertRaises(ObjNotReady) as err:
3716             obj.encode()
3717         repr(err.exception)
3718         value = d.draw(datetimes(min_value=self.min_datetime))
3719         obj = self.base_klass(value)
3720         self.assertTrue(obj.ready)
3721         repr(obj)
3722         list(obj.pps())
3723         pprint(obj, big_blobs=True, with_decode_path=True)
3724
3725     @given(data_strategy())
3726     def test_comparison(self, d):
3727         value1 = d.draw(datetimes(
3728             min_value=self.min_datetime,
3729             max_value=self.max_datetime,
3730         ))
3731         value2 = d.draw(datetimes(
3732             min_value=self.min_datetime,
3733             max_value=self.max_datetime,
3734         ))
3735         tag1 = d.draw(binary(min_size=1))
3736         tag2 = d.draw(binary(min_size=1))
3737         if self.omit_ms:
3738             value1 = value1.replace(microsecond=0)
3739             value2 = value2.replace(microsecond=0)
3740         obj1 = self.base_klass(value1)
3741         obj2 = self.base_klass(value2)
3742         self.assertEqual(obj1 == obj2, value1 == value2)
3743         self.assertEqual(obj1 != obj2, value1 != value2)
3744         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
3745         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3746         obj1 = self.base_klass(value1, impl=tag1)
3747         obj2 = self.base_klass(value1, impl=tag2)
3748         self.assertEqual(obj1 == obj2, tag1 == tag2)
3749         self.assertEqual(obj1 != obj2, tag1 != tag2)
3750
3751     @given(data_strategy())
3752     def test_call(self, d):
3753         (
3754             value_initial,
3755             impl_initial,
3756             expl_initial,
3757             default_initial,
3758             optional_initial,
3759             _decoded_initial,
3760         ) = d.draw(generalized_time_values_strategy(
3761             min_datetime=self.min_datetime,
3762             max_datetime=self.max_datetime,
3763             omit_ms=self.omit_ms,
3764         ))
3765         obj_initial = self.base_klass(
3766             value=value_initial,
3767             impl=impl_initial,
3768             expl=expl_initial,
3769             default=default_initial,
3770             optional=optional_initial or False,
3771             _decoded=_decoded_initial,
3772         )
3773         (
3774             value,
3775             impl,
3776             expl,
3777             default,
3778             optional,
3779             _decoded,
3780         ) = d.draw(generalized_time_values_strategy(
3781             min_datetime=self.min_datetime,
3782             max_datetime=self.max_datetime,
3783             omit_ms=self.omit_ms,
3784             do_expl=impl_initial is None,
3785         ))
3786         obj = obj_initial(
3787             value=value,
3788             impl=impl,
3789             expl=expl,
3790             default=default,
3791             optional=optional,
3792         )
3793         if obj.ready:
3794             value_expected = default if value is None else value
3795             value_expected = (
3796                 default_initial if value_expected is None
3797                 else value_expected
3798             )
3799             self.assertEqual(obj, value_expected)
3800         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3801         self.assertEqual(obj.expl_tag, expl or expl_initial)
3802         self.assertEqual(
3803             obj.default,
3804             default_initial if default is None else default,
3805         )
3806         if obj.default is None:
3807             optional = optional_initial if optional is None else optional
3808             optional = False if optional is None else optional
3809         else:
3810             optional = True
3811         self.assertEqual(obj.optional, optional)
3812
3813     @given(data_strategy())
3814     def test_copy(self, d):
3815         values = d.draw(generalized_time_values_strategy(
3816             min_datetime=self.min_datetime,
3817             max_datetime=self.max_datetime,
3818         ))
3819         obj = self.base_klass(*values)
3820         for copy_func in copy_funcs:
3821             obj_copied = copy_func(obj)
3822             self.assert_copied_basic_fields(obj, obj_copied)
3823             self.assertEqual(obj._value, obj_copied._value)
3824
3825     @given(data_strategy())
3826     def test_stripped(self, d):
3827         value = d.draw(datetimes(
3828             min_value=self.min_datetime,
3829             max_value=self.max_datetime,
3830         ))
3831         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3832         obj = self.base_klass(value, impl=tag_impl)
3833         with self.assertRaises(NotEnoughData):
3834             obj.decode(obj.encode()[:-1])
3835
3836     @given(data_strategy())
3837     def test_stripped_expl(self, d):
3838         value = d.draw(datetimes(
3839             min_value=self.min_datetime,
3840             max_value=self.max_datetime,
3841         ))
3842         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3843         obj = self.base_klass(value, expl=tag_expl)
3844         with self.assertRaises(NotEnoughData):
3845             obj.decode(obj.encode()[:-1])
3846
3847     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3848     @given(data_strategy())
3849     def test_symmetric(self, d):
3850         values = d.draw(generalized_time_values_strategy(
3851             min_datetime=self.min_datetime,
3852             max_datetime=self.max_datetime,
3853         ))
3854         value = d.draw(datetimes(
3855             min_value=self.min_datetime,
3856             max_value=self.max_datetime,
3857         ))
3858         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3859         offset = d.draw(integers(min_value=0))
3860         tail_junk = d.draw(binary(max_size=5))
3861         _, _, _, default, optional, _decoded = values
3862         obj = self.base_klass(
3863             value=value,
3864             default=default,
3865             optional=optional,
3866             _decoded=_decoded,
3867         )
3868         repr(obj)
3869         list(obj.pps())
3870         pprint(obj, big_blobs=True, with_decode_path=True)
3871         self.assertFalse(obj.expled)
3872         obj_encoded = obj.encode()
3873         self.additional_symmetric_check(value, obj_encoded)
3874         obj_expled = obj(value, expl=tag_expl)
3875         self.assertTrue(obj_expled.expled)
3876         repr(obj_expled)
3877         list(obj_expled.pps())
3878         pprint(obj_expled, big_blobs=True, with_decode_path=True)
3879         obj_expled_encoded = obj_expled.encode()
3880         ctx_copied = deepcopy(ctx_dummy)
3881         obj_decoded, tail = obj_expled.decode(
3882             obj_expled_encoded + tail_junk,
3883             offset=offset,
3884             ctx=ctx_copied,
3885         )
3886         self.assertDictEqual(ctx_copied, ctx_dummy)
3887         repr(obj_decoded)
3888         list(obj_decoded.pps())
3889         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
3890         self.assertEqual(tail, tail_junk)
3891         self.assertEqual(obj_decoded, obj_expled)
3892         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
3893         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
3894         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3895         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3896         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3897         self.assertEqual(
3898             obj_decoded.expl_llen,
3899             len(len_encode(len(obj_encoded))),
3900         )
3901         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3902         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3903         self.assertEqual(
3904             obj_decoded.offset,
3905             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3906         )
3907         self.assertEqual(obj_decoded.expl_offset, offset)
3908         assert_exceeding_data(
3909             self,
3910             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
3911             tail_junk,
3912         )
3913
3914
3915 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
3916     base_klass = GeneralizedTime
3917     omit_ms = False
3918     min_datetime = datetime(1900, 1, 1)
3919     max_datetime = datetime(9999, 12, 31)
3920
3921     def additional_symmetric_check(self, value, obj_encoded):
3922         if value.microsecond > 0:
3923             self.assertFalse(obj_encoded.endswith(b"0Z"))
3924
3925     def test_x690_vector_valid(self):
3926         for data in ((
3927                 b"19920521000000Z",
3928                 b"19920622123421Z",
3929                 b"19920722132100.3Z",
3930         )):
3931             GeneralizedTime(data)
3932
3933     def test_x690_vector_invalid(self):
3934         for data in ((
3935                 b"19920520240000Z",
3936                 b"19920622123421.0Z",
3937                 b"19920722132100.30Z",
3938         )):
3939             with self.assertRaises(DecodeError) as err:
3940                 GeneralizedTime(data)
3941             repr(err.exception)
3942
3943     def test_go_vectors_invalid(self):
3944         for data in ((
3945                 b"20100102030405",
3946                 b"00000100000000Z",
3947                 b"20101302030405Z",
3948                 b"20100002030405Z",
3949                 b"20100100030405Z",
3950                 b"20100132030405Z",
3951                 b"20100231030405Z",
3952                 b"20100102240405Z",
3953                 b"20100102036005Z",
3954                 b"20100102030460Z",
3955                 b"-20100102030410Z",
3956                 b"2010-0102030410Z",
3957                 b"2010-0002030410Z",
3958                 b"201001-02030410Z",
3959                 b"20100102-030410Z",
3960                 b"2010010203-0410Z",
3961                 b"201001020304-10Z",
3962                 # These ones are INVALID in *DER*, but accepted
3963                 # by Go's encoding/asn1
3964                 b"20100102030405+0607",
3965                 b"20100102030405-0607",
3966         )):
3967             with self.assertRaises(DecodeError) as err:
3968                 GeneralizedTime(data)
3969             repr(err.exception)
3970
3971     def test_go_vectors_valid(self):
3972         self.assertEqual(
3973             GeneralizedTime(b"20100102030405Z").todatetime(),
3974             datetime(2010, 1, 2, 3, 4, 5, 0),
3975         )
3976
3977     @given(
3978         binary(
3979             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3980             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3981         ),
3982         binary(min_size=1, max_size=1),
3983         binary(
3984             min_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3985             max_size=(LEN_YYYYMMDDHHMMSSZ - 1) // 2,
3986         ),
3987     )
3988     def test_junk(self, part0, part1, part2):
3989         junk = part0 + part1 + part2
3990         assume(not (set(junk) <= set(digits.encode("ascii"))))
3991         with self.assertRaises(DecodeError):
3992             GeneralizedTime().decode(
3993                 GeneralizedTime.tag_default +
3994                 len_encode(len(junk)) +
3995                 junk
3996             )
3997
3998     @given(
3999         binary(
4000             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4001             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4002         ),
4003         binary(min_size=1, max_size=1),
4004         binary(
4005             min_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4006             max_size=(LEN_YYYYMMDDHHMMSSDMZ - 1) // 2,
4007         ),
4008     )
4009     def test_junk_dm(self, part0, part1, part2):
4010         junk = part0 + part1 + part2
4011         assume(not (set(junk) <= set(digits.encode("ascii"))))
4012         with self.assertRaises(DecodeError):
4013             GeneralizedTime().decode(
4014                 GeneralizedTime.tag_default +
4015                 len_encode(len(junk)) +
4016                 junk
4017             )
4018
4019     def test_ns_fractions(self):
4020         GeneralizedTime(b"20010101000000.000001Z")
4021         with assertRaisesRegex(self, DecodeError, "only microsecond fractions"):
4022             GeneralizedTime(b"20010101000000.0000001Z")
4023
4024
4025 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
4026     base_klass = UTCTime
4027     omit_ms = True
4028     min_datetime = datetime(2000, 1, 1)
4029     max_datetime = datetime(2049, 12, 31)
4030
4031     def additional_symmetric_check(self, value, obj_encoded):
4032         pass
4033
4034     def test_x690_vector_valid(self):
4035         for data in ((
4036                 b"920521000000Z",
4037                 b"920622123421Z",
4038                 b"920722132100Z",
4039         )):
4040             UTCTime(data)
4041
4042     def test_x690_vector_invalid(self):
4043         for data in ((
4044                 b"920520240000Z",
4045                 b"9207221321Z",
4046         )):
4047             with self.assertRaises(DecodeError) as err:
4048                 UTCTime(data)
4049             repr(err.exception)
4050
4051     def test_go_vectors_invalid(self):
4052         for data in ((
4053                 b"a10506234540Z",
4054                 b"91a506234540Z",
4055                 b"9105a6234540Z",
4056                 b"910506a34540Z",
4057                 b"910506334a40Z",
4058                 b"91050633444aZ",
4059                 b"910506334461Z",
4060                 b"910506334400Za",
4061                 b"000100000000Z",
4062                 b"101302030405Z",
4063                 b"100002030405Z",
4064                 b"100100030405Z",
4065                 b"100132030405Z",
4066                 b"100231030405Z",
4067                 b"100102240405Z",
4068                 b"100102036005Z",
4069                 b"100102030460Z",
4070                 b"-100102030410Z",
4071                 b"10-0102030410Z",
4072                 b"10-0002030410Z",
4073                 b"1001-02030410Z",
4074                 b"100102-030410Z",
4075                 b"10010203-0410Z",
4076                 b"1001020304-10Z",
4077                 # These ones are INVALID in *DER*, but accepted
4078                 # by Go's encoding/asn1
4079                 b"910506164540-0700",
4080                 b"910506164540+0730",
4081                 b"9105062345Z",
4082                 b"5105062345Z",
4083         )):
4084             with self.assertRaises(DecodeError) as err:
4085                 UTCTime(data)
4086             repr(err.exception)
4087
4088     def test_go_vectors_valid(self):
4089         self.assertEqual(
4090             UTCTime(b"910506234540Z").todatetime(),
4091             datetime(1991, 5, 6, 23, 45, 40, 0),
4092         )
4093
4094     @given(integers(min_value=0, max_value=49))
4095     def test_pre50(self, year):
4096         self.assertEqual(
4097             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4098             2000 + year,
4099         )
4100
4101     @given(integers(min_value=50, max_value=99))
4102     def test_post50(self, year):
4103         self.assertEqual(
4104             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
4105             1900 + year,
4106         )
4107
4108     @given(
4109         binary(
4110             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4111             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4112         ),
4113         binary(min_size=1, max_size=1),
4114         binary(
4115             min_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4116             max_size=(LEN_YYMMDDHHMMSSZ - 1) // 2,
4117         ),
4118     )
4119     def test_junk(self, part0, part1, part2):
4120         junk = part0 + part1 + part2
4121         assume(not (set(junk) <= set(digits.encode("ascii"))))
4122         with self.assertRaises(DecodeError):
4123             UTCTime().decode(
4124                 UTCTime.tag_default +
4125                 len_encode(len(junk)) +
4126                 junk
4127             )
4128
4129
4130 @composite
4131 def any_values_strategy(draw, do_expl=False):
4132     value = draw(one_of(none(), binary()))
4133     expl = None
4134     if do_expl:
4135         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4136     optional = draw(one_of(none(), booleans()))
4137     _decoded = (
4138         draw(integers(min_value=0)),
4139         draw(integers(min_value=0)),
4140         draw(integers(min_value=0)),
4141     )
4142     return (value, expl, optional, _decoded)
4143
4144
4145 class AnyInherited(Any):
4146     pass
4147
4148
4149 class TestAny(CommonMixin, TestCase):
4150     base_klass = Any
4151
4152     def test_invalid_value_type(self):
4153         with self.assertRaises(InvalidValueType) as err:
4154             Any(123)
4155         repr(err.exception)
4156
4157     @given(booleans())
4158     def test_optional(self, optional):
4159         obj = Any(optional=optional)
4160         self.assertEqual(obj.optional, optional)
4161
4162     @given(binary())
4163     def test_ready(self, value):
4164         obj = Any()
4165         self.assertFalse(obj.ready)
4166         repr(obj)
4167         list(obj.pps())
4168         pprint(obj, big_blobs=True, with_decode_path=True)
4169         with self.assertRaises(ObjNotReady) as err:
4170             obj.encode()
4171         repr(err.exception)
4172         obj = Any(value)
4173         self.assertTrue(obj.ready)
4174         repr(obj)
4175         list(obj.pps())
4176         pprint(obj, big_blobs=True, with_decode_path=True)
4177
4178     @given(integers())
4179     def test_basic(self, value):
4180         integer_encoded = Integer(value).encode()
4181         for obj in (
4182                 Any(integer_encoded),
4183                 Any(Integer(value)),
4184                 Any(Any(Integer(value))),
4185         ):
4186             self.assertSequenceEqual(bytes(obj), integer_encoded)
4187             self.assertEqual(
4188                 obj.decode(obj.encode())[0].vlen,
4189                 len(integer_encoded),
4190             )
4191             repr(obj)
4192             list(obj.pps())
4193             pprint(obj, big_blobs=True, with_decode_path=True)
4194             self.assertSequenceEqual(obj.encode(), integer_encoded)
4195
4196     @given(binary(), binary())
4197     def test_comparison(self, value1, value2):
4198         for klass in (Any, AnyInherited):
4199             obj1 = klass(value1)
4200             obj2 = klass(value2)
4201             self.assertEqual(obj1 == obj2, value1 == value2)
4202             self.assertEqual(obj1 != obj2, value1 != value2)
4203             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
4204
4205     @given(data_strategy())
4206     def test_call(self, d):
4207         for klass in (Any, AnyInherited):
4208             (
4209                 value_initial,
4210                 expl_initial,
4211                 optional_initial,
4212                 _decoded_initial,
4213             ) = d.draw(any_values_strategy())
4214             obj_initial = klass(
4215                 value_initial,
4216                 expl_initial,
4217                 optional_initial or False,
4218                 _decoded_initial,
4219             )
4220             (
4221                 value,
4222                 expl,
4223                 optional,
4224                 _decoded,
4225             ) = d.draw(any_values_strategy(do_expl=True))
4226             obj = obj_initial(value, expl, optional)
4227             if obj.ready:
4228                 value_expected = None if value is None else value
4229                 self.assertEqual(obj, value_expected)
4230             self.assertEqual(obj.expl_tag, expl or expl_initial)
4231             if obj.default is None:
4232                 optional = optional_initial if optional is None else optional
4233                 optional = False if optional is None else optional
4234             self.assertEqual(obj.optional, optional)
4235
4236     def test_simultaneous_impl_expl(self):
4237         # override it, as Any does not have implicit tag
4238         pass
4239
4240     def test_decoded(self):
4241         # override it, as Any does not have implicit tag
4242         pass
4243
4244     @given(any_values_strategy())
4245     def test_copy(self, values):
4246         for klass in (Any, AnyInherited):
4247             obj = klass(*values)
4248             for copy_func in copy_funcs:
4249                 obj_copied = copy_func(obj)
4250                 self.assert_copied_basic_fields(obj, obj_copied)
4251                 self.assertEqual(obj._value, obj_copied._value)
4252
4253     @given(binary().map(OctetString))
4254     def test_stripped(self, value):
4255         obj = Any(value)
4256         with self.assertRaises(NotEnoughData):
4257             obj.decode(obj.encode()[:-1])
4258
4259     @given(
4260         binary(),
4261         integers(min_value=1).map(tag_ctxc),
4262     )
4263     def test_stripped_expl(self, value, tag_expl):
4264         obj = Any(value, expl=tag_expl)
4265         with self.assertRaises(NotEnoughData):
4266             obj.decode(obj.encode()[:-1])
4267
4268     @given(
4269         integers(min_value=31),
4270         integers(min_value=0),
4271         decode_path_strat,
4272     )
4273     def test_bad_tag(self, tag, offset, decode_path):
4274         with self.assertRaises(DecodeError) as err:
4275             Any().decode(
4276                 tag_encode(tag)[:-1],
4277                 offset=offset,
4278                 decode_path=decode_path,
4279             )
4280         repr(err.exception)
4281         self.assertEqual(err.exception.offset, offset)
4282         self.assertEqual(err.exception.decode_path, decode_path)
4283
4284     @given(
4285         integers(min_value=128),
4286         integers(min_value=0),
4287         decode_path_strat,
4288     )
4289     def test_bad_len(self, l, offset, decode_path):
4290         with self.assertRaises(DecodeError) as err:
4291             Any().decode(
4292                 Any.tag_default + len_encode(l)[:-1],
4293                 offset=offset,
4294                 decode_path=decode_path,
4295             )
4296         repr(err.exception)
4297         self.assertEqual(err.exception.offset, offset)
4298         self.assertEqual(err.exception.decode_path, decode_path)
4299
4300     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4301     @given(
4302         any_values_strategy(),
4303         integers().map(lambda x: Integer(x).encode()),
4304         integers(min_value=1).map(tag_ctxc),
4305         integers(min_value=0),
4306         binary(max_size=5),
4307     )
4308     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
4309         for klass in (Any, AnyInherited):
4310             _, _, optional, _decoded = values
4311             obj = klass(value=value, optional=optional, _decoded=_decoded)
4312             repr(obj)
4313             list(obj.pps())
4314             pprint(obj, big_blobs=True, with_decode_path=True)
4315             self.assertFalse(obj.expled)
4316             obj_encoded = obj.encode()
4317             obj_expled = obj(value, expl=tag_expl)
4318             self.assertTrue(obj_expled.expled)
4319             repr(obj_expled)
4320             list(obj_expled.pps())
4321             pprint(obj_expled, big_blobs=True, with_decode_path=True)
4322             obj_expled_encoded = obj_expled.encode()
4323             ctx_copied = deepcopy(ctx_dummy)
4324             obj_decoded, tail = obj_expled.decode(
4325                 obj_expled_encoded + tail_junk,
4326                 offset=offset,
4327                 ctx=ctx_copied,
4328             )
4329             self.assertDictEqual(ctx_copied, ctx_dummy)
4330             repr(obj_decoded)
4331             list(obj_decoded.pps())
4332             pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4333             self.assertEqual(tail, tail_junk)
4334             self.assertEqual(obj_decoded, obj_expled)
4335             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
4336             self.assertEqual(bytes(obj_decoded), bytes(obj))
4337             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4338             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4339             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4340             self.assertEqual(
4341                 obj_decoded.expl_llen,
4342                 len(len_encode(len(obj_encoded))),
4343             )
4344             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4345             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4346             self.assertEqual(
4347                 obj_decoded.offset,
4348                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4349             )
4350             self.assertEqual(obj_decoded.expl_offset, offset)
4351             self.assertEqual(obj_decoded.tlen, 0)
4352             self.assertEqual(obj_decoded.llen, 0)
4353             self.assertEqual(obj_decoded.vlen, len(value))
4354             assert_exceeding_data(
4355                 self,
4356                 lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
4357                 tail_junk,
4358             )
4359
4360     @given(
4361         integers(min_value=1).map(tag_ctxc),
4362         integers(min_value=0, max_value=3),
4363         integers(min_value=0),
4364         decode_path_strat,
4365         binary(),
4366     )
4367     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
4368         chunk = Boolean(False, expl=expl).encode()
4369         encoded = (
4370             OctetString.tag_default +
4371             LENINDEF +
4372             b"".join([chunk] * chunks) +
4373             EOC
4374         )
4375         with self.assertRaises(LenIndefForm):
4376             Any().decode(
4377                 encoded + junk,
4378                 offset=offset,
4379                 decode_path=decode_path,
4380             )
4381         obj, tail = Any().decode(
4382             encoded + junk,
4383             offset=offset,
4384             decode_path=decode_path,
4385             ctx={"bered": True},
4386         )
4387         self.assertSequenceEqual(tail, junk)
4388         self.assertEqual(obj.offset, offset)
4389         self.assertEqual(obj.tlvlen, len(encoded))
4390         self.assertTrue(obj.lenindef)
4391         self.assertFalse(obj.ber_encoded)
4392         self.assertTrue(obj.bered)
4393         obj = copy(obj)
4394         self.assertTrue(obj.lenindef)
4395         self.assertFalse(obj.ber_encoded)
4396         self.assertTrue(obj.bered)
4397         repr(obj)
4398         list(obj.pps())
4399         pprint(obj, big_blobs=True, with_decode_path=True)
4400         with self.assertRaises(NotEnoughData) as err:
4401             Any().decode(
4402                 encoded[:-1],
4403                 offset=offset,
4404                 decode_path=decode_path,
4405                 ctx={"bered": True},
4406             )
4407         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
4408         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
4409
4410         class SeqOf(SequenceOf):
4411             schema = Boolean(expl=expl)
4412
4413         class Seq(Sequence):
4414             schema = (
4415                 ("type", ObjectIdentifier(defines=((("value",), {
4416                     ObjectIdentifier("1.2.3"): SeqOf(impl=OctetString.tag_default),
4417                 }),))),
4418                 ("value", Any()),
4419             )
4420         seq = Seq((
4421             ("type", ObjectIdentifier("1.2.3")),
4422             ("value", Any(encoded)),
4423         ))
4424         seq_encoded = seq.encode()
4425         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
4426         self.assertIsNotNone(seq_decoded["value"].defined)
4427         repr(seq_decoded)
4428         list(seq_decoded.pps())
4429         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
4430         self.assertTrue(seq_decoded.bered)
4431         self.assertFalse(seq_decoded["type"].bered)
4432         self.assertTrue(seq_decoded["value"].bered)
4433
4434         chunk = chunk[:-1] + b"\x01"
4435         chunks = b"".join([chunk] * (chunks + 1))
4436         encoded = OctetString.tag_default + len_encode(len(chunks)) + chunks
4437         seq = Seq((
4438             ("type", ObjectIdentifier("1.2.3")),
4439             ("value", Any(encoded)),
4440         ))
4441         seq_encoded = seq.encode()
4442         seq_decoded, _ = Seq().decode(seq_encoded, ctx={"bered": True})
4443         self.assertIsNotNone(seq_decoded["value"].defined)
4444         repr(seq_decoded)
4445         list(seq_decoded.pps())
4446         pprint(seq_decoded, big_blobs=True, with_decode_path=True)
4447         self.assertTrue(seq_decoded.bered)
4448         self.assertFalse(seq_decoded["type"].bered)
4449         self.assertTrue(seq_decoded["value"].bered)
4450
4451
4452 @composite
4453 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
4454     if schema is None:
4455         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
4456         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
4457             one_of(
4458                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
4459                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
4460             ),
4461             min_size=len(names),
4462             max_size=len(names),
4463         ))]
4464         schema = [
4465             (name, Integer(**tag_kwargs))
4466             for name, tag_kwargs in zip(names, tags)
4467         ]
4468     value = None
4469     if value_required or draw(booleans()):
4470         value = draw(tuples(
4471             sampled_from([name for name, _ in schema]),
4472             integers().map(Integer),
4473         ))
4474     expl = None
4475     if do_expl:
4476         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4477     default = draw(one_of(
4478         none(),
4479         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
4480     ))
4481     optional = draw(one_of(none(), booleans()))
4482     _decoded = (
4483         draw(integers(min_value=0)),
4484         draw(integers(min_value=0)),
4485         draw(integers(min_value=0)),
4486     )
4487     return (schema, value, expl, default, optional, _decoded)
4488
4489
4490 class ChoiceInherited(Choice):
4491     pass
4492
4493
4494 class TestChoice(CommonMixin, TestCase):
4495     class Wahl(Choice):
4496         schema = (("whatever", Boolean()),)
4497     base_klass = Wahl
4498
4499     def test_schema_required(self):
4500         with assertRaisesRegex(self, ValueError, "schema must be specified"):
4501             Choice()
4502
4503     def test_impl_forbidden(self):
4504         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
4505             Choice(impl=b"whatever")
4506
4507     def test_invalid_value_type(self):
4508         with self.assertRaises(InvalidValueType) as err:
4509             self.base_klass(123)
4510         repr(err.exception)
4511         with self.assertRaises(ObjUnknown) as err:
4512             self.base_klass(("whenever", Boolean(False)))
4513         repr(err.exception)
4514         with self.assertRaises(InvalidValueType) as err:
4515             self.base_klass(("whatever", Integer(123)))
4516         repr(err.exception)
4517
4518     @given(booleans())
4519     def test_optional(self, optional):
4520         obj = self.base_klass(
4521             default=self.base_klass(("whatever", Boolean(False))),
4522             optional=optional,
4523         )
4524         self.assertTrue(obj.optional)
4525
4526     @given(booleans())
4527     def test_ready(self, value):
4528         obj = self.base_klass()
4529         self.assertFalse(obj.ready)
4530         repr(obj)
4531         list(obj.pps())
4532         pprint(obj, big_blobs=True, with_decode_path=True)
4533         self.assertIsNone(obj["whatever"])
4534         with self.assertRaises(ObjNotReady) as err:
4535             obj.encode()
4536         repr(err.exception)
4537         obj["whatever"] = Boolean()
4538         self.assertFalse(obj.ready)
4539         repr(obj)
4540         list(obj.pps())
4541         pprint(obj, big_blobs=True, with_decode_path=True)
4542         obj["whatever"] = Boolean(value)
4543         self.assertTrue(obj.ready)
4544         repr(obj)
4545         list(obj.pps())
4546         pprint(obj, big_blobs=True, with_decode_path=True)
4547
4548     @given(booleans(), booleans())
4549     def test_comparison(self, value1, value2):
4550         class WahlInherited(self.base_klass):
4551             pass
4552         for klass in (self.base_klass, WahlInherited):
4553             obj1 = klass(("whatever", Boolean(value1)))
4554             obj2 = klass(("whatever", Boolean(value2)))
4555             self.assertEqual(obj1 == obj2, value1 == value2)
4556             self.assertEqual(obj1 != obj2, value1 != value2)
4557             self.assertEqual(obj1 == obj2._value, value1 == value2)
4558             self.assertFalse(obj1 == obj2._value[1])
4559
4560     @given(data_strategy())
4561     def test_call(self, d):
4562         for klass in (Choice, ChoiceInherited):
4563             (
4564                 schema_initial,
4565                 value_initial,
4566                 expl_initial,
4567                 default_initial,
4568                 optional_initial,
4569                 _decoded_initial,
4570             ) = d.draw(choice_values_strategy())
4571
4572             class Wahl(klass):
4573                 schema = schema_initial
4574             obj_initial = Wahl(
4575                 value=value_initial,
4576                 expl=expl_initial,
4577                 default=default_initial,
4578                 optional=optional_initial or False,
4579                 _decoded=_decoded_initial,
4580             )
4581             (
4582                 _,
4583                 value,
4584                 expl,
4585                 default,
4586                 optional,
4587                 _decoded,
4588             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
4589             obj = obj_initial(value, expl, default, optional)
4590             if obj.ready:
4591                 value_expected = default if value is None else value
4592                 value_expected = (
4593                     default_initial if value_expected is None
4594                     else value_expected
4595                 )
4596                 self.assertEqual(obj.choice, value_expected[0])
4597                 self.assertEqual(obj.value, int(value_expected[1]))
4598             self.assertEqual(obj.expl_tag, expl or expl_initial)
4599             default_expect = default_initial if default is None else default
4600             if default_expect is not None:
4601                 self.assertEqual(obj.default.choice, default_expect[0])
4602                 self.assertEqual(obj.default.value, int(default_expect[1]))
4603             if obj.default is None:
4604                 optional = optional_initial if optional is None else optional
4605                 optional = False if optional is None else optional
4606             else:
4607                 optional = True
4608             self.assertEqual(obj.optional, optional)
4609             self.assertEqual(obj.specs, obj_initial.specs)
4610
4611     def test_simultaneous_impl_expl(self):
4612         # override it, as Any does not have implicit tag
4613         pass
4614
4615     def test_decoded(self):
4616         # override it, as Any does not have implicit tag
4617         pass
4618
4619     @given(choice_values_strategy())
4620     def test_copy(self, values):
4621         _schema, value, expl, default, optional, _decoded = values
4622
4623         class Wahl(self.base_klass):
4624             schema = _schema
4625         register_class(Wahl)
4626         obj = Wahl(
4627             value=value,
4628             expl=expl,
4629             default=default,
4630             optional=optional or False,
4631             _decoded=_decoded,
4632         )
4633         for copy_func in copy_funcs:
4634             obj_copied = copy_func(obj)
4635             self.assertIsNone(obj.tag)
4636             self.assertIsNone(obj_copied.tag)
4637             # hack for assert_copied_basic_fields
4638             obj.tag = "whatever"
4639             obj_copied.tag = "whatever"
4640             self.assert_copied_basic_fields(obj, obj_copied)
4641             obj.tag = None
4642             self.assertEqual(obj._value, obj_copied._value)
4643             self.assertEqual(obj.specs, obj_copied.specs)
4644
4645     @given(booleans())
4646     def test_stripped(self, value):
4647         obj = self.base_klass(("whatever", Boolean(value)))
4648         with self.assertRaises(NotEnoughData):
4649             obj.decode(obj.encode()[:-1])
4650
4651     @given(
4652         booleans(),
4653         integers(min_value=1).map(tag_ctxc),
4654     )
4655     def test_stripped_expl(self, value, tag_expl):
4656         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
4657         with self.assertRaises(NotEnoughData):
4658             obj.decode(obj.encode()[:-1])
4659
4660     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4661     @given(data_strategy())
4662     def test_symmetric(self, d):
4663         _schema, value, _, default, optional, _decoded = d.draw(
4664             choice_values_strategy(value_required=True)
4665         )
4666         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4667         offset = d.draw(integers(min_value=0))
4668         tail_junk = d.draw(binary(max_size=5))
4669
4670         class Wahl(self.base_klass):
4671             schema = _schema
4672         obj = Wahl(
4673             value=value,
4674             default=default,
4675             optional=optional,
4676             _decoded=_decoded,
4677         )
4678         repr(obj)
4679         list(obj.pps())
4680         pprint(obj, big_blobs=True, with_decode_path=True)
4681         self.assertFalse(obj.expled)
4682         obj_encoded = obj.encode()
4683         obj_expled = obj(value, expl=tag_expl)
4684         self.assertTrue(obj_expled.expled)
4685         repr(obj_expled)
4686         list(obj_expled.pps())
4687         pprint(obj_expled, big_blobs=True, with_decode_path=True)
4688         obj_expled_encoded = obj_expled.encode()
4689         ctx_copied = deepcopy(ctx_dummy)
4690         obj_decoded, tail = obj_expled.decode(
4691             obj_expled_encoded + tail_junk,
4692             offset=offset,
4693             ctx=ctx_copied,
4694         )
4695         self.assertDictEqual(ctx_copied, ctx_dummy)
4696         repr(obj_decoded)
4697         list(obj_decoded.pps())
4698         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
4699         self.assertEqual(tail, tail_junk)
4700         self.assertEqual(obj_decoded, obj_expled)
4701         self.assertEqual(obj_decoded.choice, obj_expled.choice)
4702         self.assertEqual(obj_decoded.value, obj_expled.value)
4703         self.assertEqual(obj_decoded.choice, obj.choice)
4704         self.assertEqual(obj_decoded.value, obj.value)
4705         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4706         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4707         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4708         self.assertEqual(
4709             obj_decoded.expl_llen,
4710             len(len_encode(len(obj_encoded))),
4711         )
4712         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4713         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4714         self.assertEqual(
4715             obj_decoded.offset,
4716             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4717         )
4718         self.assertEqual(obj_decoded.expl_offset, offset)
4719         self.assertSequenceEqual(
4720             obj_expled_encoded[
4721                 obj_decoded.value.fulloffset - offset:
4722                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
4723             ],
4724             obj_encoded,
4725         )
4726         assert_exceeding_data(
4727             self,
4728             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
4729             tail_junk,
4730         )
4731
4732     @given(integers())
4733     def test_set_get(self, value):
4734         class Wahl(Choice):
4735             schema = (
4736                 ("erste", Boolean()),
4737                 ("zweite", Integer()),
4738             )
4739         obj = Wahl()
4740         with self.assertRaises(ObjUnknown) as err:
4741             obj["whatever"] = "whenever"
4742         with self.assertRaises(InvalidValueType) as err:
4743             obj["zweite"] = Boolean(False)
4744         obj["zweite"] = Integer(value)
4745         repr(err.exception)
4746         with self.assertRaises(ObjUnknown) as err:
4747             obj["whatever"]
4748         repr(err.exception)
4749         self.assertIsNone(obj["erste"])
4750         self.assertEqual(obj["zweite"], Integer(value))
4751
4752     def test_tag_mismatch(self):
4753         class Wahl(Choice):
4754             schema = (
4755                 ("erste", Boolean()),
4756             )
4757         int_encoded = Integer(123).encode()
4758         bool_encoded = Boolean(False).encode()
4759         obj = Wahl()
4760         obj.decode(bool_encoded)
4761         with self.assertRaises(TagMismatch):
4762             obj.decode(int_encoded)
4763
4764     def test_tag_mismatch_underlying(self):
4765         class SeqOfBoolean(SequenceOf):
4766             schema = Boolean()
4767
4768         class SeqOfInteger(SequenceOf):
4769             schema = Integer()
4770
4771         class Wahl(Choice):
4772             schema = (
4773                 ("erste", SeqOfBoolean()),
4774             )
4775
4776         int_encoded = SeqOfInteger((Integer(123),)).encode()
4777         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
4778         obj = Wahl()
4779         obj.decode(bool_encoded)
4780         with self.assertRaises(TagMismatch) as err:
4781             obj.decode(int_encoded)
4782         self.assertEqual(err.exception.decode_path, ("erste", "0"))
4783
4784
4785 @composite
4786 def seq_values_strategy(draw, seq_klass, do_expl=False):
4787     value = None
4788     if draw(booleans()):
4789         value = seq_klass()
4790         value._value = draw(dictionaries(
4791             integers(),
4792             one_of(
4793                 booleans().map(Boolean),
4794                 integers().map(Integer),
4795             ),
4796         ))
4797     schema = None
4798     if draw(booleans()):
4799         schema = list(draw(dictionaries(
4800             integers(),
4801             one_of(
4802                 booleans().map(Boolean),
4803                 integers().map(Integer),
4804             ),
4805         )).items())
4806     impl = None
4807     expl = None
4808     if do_expl:
4809         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4810     else:
4811         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4812     default = None
4813     if draw(booleans()):
4814         default = seq_klass()
4815         default._value = draw(dictionaries(
4816             integers(),
4817             one_of(
4818                 booleans().map(Boolean),
4819                 integers().map(Integer),
4820             ),
4821         ))
4822     optional = draw(one_of(none(), booleans()))
4823     _decoded = (
4824         draw(integers(min_value=0)),
4825         draw(integers(min_value=0)),
4826         draw(integers(min_value=0)),
4827     )
4828     return (value, schema, impl, expl, default, optional, _decoded)
4829
4830
4831 @composite
4832 def sequence_strategy(draw, seq_klass):
4833     inputs = draw(lists(
4834         one_of(
4835             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
4836             tuples(just(Integer), integers(), one_of(none(), integers())),
4837         ),
4838         max_size=6,
4839     ))
4840     tags = draw(sets(
4841         integers(min_value=1),
4842         min_size=len(inputs),
4843         max_size=len(inputs),
4844     ))
4845     inits = [
4846         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4847         for tag, expled in zip(tags, draw(lists(
4848             booleans(),
4849             min_size=len(inputs),
4850             max_size=len(inputs),
4851         )))
4852     ]
4853     empties = []
4854     for i, optional in enumerate(draw(lists(
4855             sampled_from(("required", "optional", "empty")),
4856             min_size=len(inputs),
4857             max_size=len(inputs),
4858     ))):
4859         if optional in ("optional", "empty"):
4860             inits[i]["optional"] = True
4861         if optional == "empty":
4862             empties.append(i)
4863     empties = set(empties)
4864     names = list(draw(sets(
4865         text_printable,
4866         min_size=len(inputs),
4867         max_size=len(inputs),
4868     )))
4869     schema = []
4870     for i, (klass, value, default) in enumerate(inputs):
4871         schema.append((names[i], klass(default=default, **inits[i])))
4872     seq_name = draw(text_letters())
4873     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4874     seq = Seq()
4875     expects = []
4876     for i, (klass, value, default) in enumerate(inputs):
4877         name = names[i]
4878         _, spec = schema[i]
4879         expect = {
4880             "name": name,
4881             "optional": False,
4882             "presented": False,
4883             "default_value": None if spec.default is None else default,
4884             "value": None,
4885         }
4886         if i in empties:
4887             expect["optional"] = True
4888         else:
4889             expect["presented"] = True
4890             expect["value"] = value
4891             if spec.optional:
4892                 expect["optional"] = True
4893             if default is not None and default == value:
4894                 expect["presented"] = False
4895             seq[name] = klass(value)
4896         expects.append(expect)
4897     return seq, expects
4898
4899
4900 @composite
4901 def sequences_strategy(draw, seq_klass):
4902     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
4903     inits = [
4904         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4905         for tag, expled in zip(tags, draw(lists(
4906             booleans(),
4907             min_size=len(tags),
4908             max_size=len(tags),
4909         )))
4910     ]
4911     defaulted = set(
4912         i for i, is_default in enumerate(draw(lists(
4913             booleans(),
4914             min_size=len(tags),
4915             max_size=len(tags),
4916         ))) if is_default
4917     )
4918     names = list(draw(sets(
4919         text_printable,
4920         min_size=len(tags),
4921         max_size=len(tags),
4922     )))
4923     seq_expectses = draw(lists(
4924         sequence_strategy(seq_klass=seq_klass),
4925         min_size=len(tags),
4926         max_size=len(tags),
4927     ))
4928     seqs = [seq for seq, _ in seq_expectses]
4929     schema = []
4930     for i, (name, seq) in enumerate(zip(names, seqs)):
4931         schema.append((
4932             name,
4933             seq(default=(seq if i in defaulted else None), **inits[i]),
4934         ))
4935     seq_name = draw(text_letters())
4936     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4937     seq_outer = Seq()
4938     expect_outers = []
4939     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
4940         expect = {
4941             "name": name,
4942             "expects": expects_inner,
4943             "presented": False,
4944         }
4945         seq_outer[name] = seq_inner
4946         if seq_outer.specs[name].default is None:
4947             expect["presented"] = True
4948         expect_outers.append(expect)
4949     return seq_outer, expect_outers
4950
4951
4952 class SeqMixing(object):
4953     def test_invalid_value_type(self):
4954         with self.assertRaises(InvalidValueType) as err:
4955             self.base_klass(123)
4956         repr(err.exception)
4957
4958     def test_invalid_value_type_set(self):
4959         class Seq(self.base_klass):
4960             schema = (("whatever", Boolean()),)
4961         seq = Seq()
4962         with self.assertRaises(InvalidValueType) as err:
4963             seq["whatever"] = Integer(123)
4964         repr(err.exception)
4965
4966     @given(booleans())
4967     def test_optional(self, optional):
4968         obj = self.base_klass(default=self.base_klass(), optional=optional)
4969         self.assertTrue(obj.optional)
4970
4971     @given(data_strategy())
4972     def test_ready(self, d):
4973         ready = {
4974             str(i): v for i, v in enumerate(d.draw(lists(
4975                 booleans(),
4976                 min_size=1,
4977                 max_size=3,
4978             )))
4979         }
4980         non_ready = {
4981             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
4982                 booleans(),
4983                 min_size=1,
4984                 max_size=3,
4985             )))
4986         }
4987         schema_input = []
4988         for name in d.draw(permutations(
4989                 list(ready.keys()) + list(non_ready.keys()),
4990         )):
4991             schema_input.append((name, Boolean()))
4992
4993         class Seq(self.base_klass):
4994             schema = tuple(schema_input)
4995         seq = Seq()
4996         for name in ready.keys():
4997             seq[name]
4998             seq[name] = Boolean()
4999         self.assertFalse(seq.ready)
5000         repr(seq)
5001         list(seq.pps())
5002         pprint(seq, big_blobs=True, with_decode_path=True)
5003         for name, value in ready.items():
5004             seq[name] = Boolean(value)
5005         self.assertFalse(seq.ready)
5006         repr(seq)
5007         list(seq.pps())
5008         pprint(seq, big_blobs=True, with_decode_path=True)
5009         with self.assertRaises(ObjNotReady) as err:
5010             seq.encode()
5011         repr(err.exception)
5012         for name, value in non_ready.items():
5013             seq[name] = Boolean(value)
5014         self.assertTrue(seq.ready)
5015         repr(seq)
5016         list(seq.pps())
5017         pprint(seq, big_blobs=True, with_decode_path=True)
5018
5019     @given(data_strategy())
5020     def test_call(self, d):
5021         class SeqInherited(self.base_klass):
5022             pass
5023         for klass in (self.base_klass, SeqInherited):
5024             (
5025                 value_initial,
5026                 schema_initial,
5027                 impl_initial,
5028                 expl_initial,
5029                 default_initial,
5030                 optional_initial,
5031                 _decoded_initial,
5032             ) = d.draw(seq_values_strategy(seq_klass=klass))
5033             obj_initial = klass(
5034                 value_initial,
5035                 schema_initial,
5036                 impl_initial,
5037                 expl_initial,
5038                 default_initial,
5039                 optional_initial or False,
5040                 _decoded_initial,
5041             )
5042             (
5043                 value,
5044                 _,
5045                 impl,
5046                 expl,
5047                 default,
5048                 optional,
5049                 _decoded,
5050             ) = d.draw(seq_values_strategy(
5051                 seq_klass=klass,
5052                 do_expl=impl_initial is None,
5053             ))
5054             obj = obj_initial(value, impl, expl, default, optional)
5055             value_expected = default if value is None else value
5056             value_expected = (
5057                 default_initial if value_expected is None
5058                 else value_expected
5059             )
5060             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
5061             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5062             self.assertEqual(obj.expl_tag, expl or expl_initial)
5063             self.assertEqual(
5064                 {} if obj.default is None else obj.default._value,
5065                 getattr(default_initial if default is None else default, "_value", {}),
5066             )
5067             if obj.default is None:
5068                 optional = optional_initial if optional is None else optional
5069                 optional = False if optional is None else optional
5070             else:
5071                 optional = True
5072             self.assertEqual(list(obj.specs.items()), schema_initial or [])
5073             self.assertEqual(obj.optional, optional)
5074
5075     @given(data_strategy())
5076     def test_copy(self, d):
5077         class SeqInherited(self.base_klass):
5078             pass
5079         register_class(SeqInherited)
5080         for klass in (self.base_klass, SeqInherited):
5081             values = d.draw(seq_values_strategy(seq_klass=klass))
5082             obj = klass(*values)
5083             for copy_func in copy_funcs:
5084                 obj_copied = copy_func(obj)
5085                 self.assert_copied_basic_fields(obj, obj_copied)
5086                 self.assertEqual(obj.specs, obj_copied.specs)
5087                 self.assertEqual(obj._value, obj_copied._value)
5088
5089     @given(data_strategy())
5090     def test_stripped(self, d):
5091         value = d.draw(integers())
5092         tag_impl = tag_encode(d.draw(integers(min_value=1)))
5093
5094         class Seq(self.base_klass):
5095             impl = tag_impl
5096             schema = (("whatever", Integer()),)
5097         seq = Seq()
5098         seq["whatever"] = Integer(value)
5099         with self.assertRaises(NotEnoughData):
5100             seq.decode(seq.encode()[:-1])
5101
5102     @given(data_strategy())
5103     def test_stripped_expl(self, d):
5104         value = d.draw(integers())
5105         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
5106
5107         class Seq(self.base_klass):
5108             expl = tag_expl
5109             schema = (("whatever", Integer()),)
5110         seq = Seq()
5111         seq["whatever"] = Integer(value)
5112         with self.assertRaises(NotEnoughData):
5113             seq.decode(seq.encode()[:-1])
5114
5115     @given(binary(min_size=2))
5116     def test_non_tag_mismatch_raised(self, junk):
5117         try:
5118             _, _, len_encoded = tag_strip(memoryview(junk))
5119             len_decode(len_encoded)
5120         except Exception:
5121             assume(True)
5122         else:
5123             assume(False)
5124
5125         class Seq(self.base_klass):
5126             schema = (
5127                 ("whatever", Integer()),
5128                 ("junk", Any()),
5129                 ("whenever", Integer()),
5130             )
5131         seq = Seq()
5132         seq["whatever"] = Integer(123)
5133         seq["junk"] = Any(junk)
5134         seq["whenever"] = Integer(123)
5135         with self.assertRaises(DecodeError):
5136             seq.decode(seq.encode())
5137
5138     @given(
5139         integers(min_value=31),
5140         integers(min_value=0),
5141         decode_path_strat,
5142     )
5143     def test_bad_tag(self, tag, offset, decode_path):
5144         with self.assertRaises(DecodeError) as err:
5145             self.base_klass().decode(
5146                 tag_encode(tag)[:-1],
5147                 offset=offset,
5148                 decode_path=decode_path,
5149             )
5150         repr(err.exception)
5151         self.assertEqual(err.exception.offset, offset)
5152         self.assertEqual(err.exception.decode_path, decode_path)
5153
5154     @given(
5155         integers(min_value=128),
5156         integers(min_value=0),
5157         decode_path_strat,
5158     )
5159     def test_bad_len(self, l, offset, decode_path):
5160         with self.assertRaises(DecodeError) as err:
5161             self.base_klass().decode(
5162                 self.base_klass.tag_default + len_encode(l)[:-1],
5163                 offset=offset,
5164                 decode_path=decode_path,
5165             )
5166         repr(err.exception)
5167         self.assertEqual(err.exception.offset, offset)
5168         self.assertEqual(err.exception.decode_path, decode_path)
5169
5170     def _assert_expects(self, seq, expects):
5171         for expect in expects:
5172             self.assertEqual(
5173                 seq.specs[expect["name"]].optional,
5174                 expect["optional"],
5175             )
5176             if expect["default_value"] is not None:
5177                 self.assertEqual(
5178                     seq.specs[expect["name"]].default,
5179                     expect["default_value"],
5180                 )
5181             if expect["presented"]:
5182                 self.assertIn(expect["name"], seq)
5183                 self.assertEqual(seq[expect["name"]], expect["value"])
5184
5185     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5186     @given(data_strategy())
5187     def test_symmetric(self, d):
5188         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
5189         tail_junk = d.draw(binary(max_size=5))
5190         self.assertTrue(seq.ready)
5191         self.assertFalse(seq.decoded)
5192         self._assert_expects(seq, expects)
5193         repr(seq)
5194         list(seq.pps())
5195         pprint(seq, big_blobs=True, with_decode_path=True)
5196         self.assertTrue(seq.ready)
5197         seq_encoded = seq.encode()
5198         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
5199         self.assertFalse(seq_decoded.lenindef)
5200         self.assertFalse(seq_decoded.ber_encoded)
5201         self.assertFalse(seq_decoded.bered)
5202
5203         t, _, lv = tag_strip(seq_encoded)
5204         _, _, v = len_decode(lv)
5205         seq_encoded_lenindef = t + LENINDEF + v + EOC
5206         ctx_copied = deepcopy(ctx_dummy)
5207         ctx_copied["bered"] = True
5208         seq_decoded_lenindef, tail_lenindef = seq.decode(
5209             seq_encoded_lenindef + tail_junk,
5210             ctx=ctx_copied,
5211         )
5212         del ctx_copied["bered"]
5213         self.assertDictEqual(ctx_copied, ctx_dummy)
5214         self.assertTrue(seq_decoded_lenindef.lenindef)
5215         self.assertTrue(seq_decoded_lenindef.bered)
5216         seq_decoded_lenindef = copy(seq_decoded_lenindef)
5217         self.assertTrue(seq_decoded_lenindef.lenindef)
5218         self.assertTrue(seq_decoded_lenindef.bered)
5219         with self.assertRaises(DecodeError):
5220             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
5221         with self.assertRaises(DecodeError):
5222             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
5223         repr(seq_decoded_lenindef)
5224         list(seq_decoded_lenindef.pps())
5225         pprint(seq_decoded_lenindef, big_blobs=True, with_decode_path=True)
5226         self.assertTrue(seq_decoded_lenindef.ready)
5227
5228         for decoded, decoded_tail, encoded in (
5229                 (seq_decoded, tail, seq_encoded),
5230                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
5231         ):
5232             self.assertEqual(decoded_tail, tail_junk)
5233             self._assert_expects(decoded, expects)
5234             self.assertEqual(seq, decoded)
5235             self.assertEqual(decoded.encode(), seq_encoded)
5236             self.assertEqual(decoded.tlvlen, len(encoded))
5237             for expect in expects:
5238                 if not expect["presented"]:
5239                     self.assertNotIn(expect["name"], decoded)
5240                     continue
5241                 self.assertIn(expect["name"], decoded)
5242                 obj = decoded[expect["name"]]
5243                 self.assertTrue(obj.decoded)
5244                 offset = obj.expl_offset if obj.expled else obj.offset
5245                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
5246                 self.assertSequenceEqual(
5247                     seq_encoded[offset:offset + tlvlen],
5248                     obj.encode(),
5249                 )
5250
5251         assert_exceeding_data(
5252             self,
5253             lambda: seq.decod(seq_encoded_lenindef + tail_junk, ctx={"bered": True}),
5254             tail_junk,
5255         )
5256
5257     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5258     @given(data_strategy())
5259     def test_symmetric_with_seq(self, d):
5260         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
5261         self.assertTrue(seq.ready)
5262         seq_encoded = seq.encode()
5263         seq_decoded, tail = seq.decode(seq_encoded)
5264         self.assertEqual(tail, b"")
5265         self.assertTrue(seq.ready)
5266         self.assertEqual(seq, seq_decoded)
5267         self.assertEqual(seq_decoded.encode(), seq_encoded)
5268         for expect_outer in expect_outers:
5269             if not expect_outer["presented"]:
5270                 self.assertNotIn(expect_outer["name"], seq_decoded)
5271                 continue
5272             self.assertIn(expect_outer["name"], seq_decoded)
5273             obj = seq_decoded[expect_outer["name"]]
5274             self.assertTrue(obj.decoded)
5275             offset = obj.expl_offset if obj.expled else obj.offset
5276             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
5277             self.assertSequenceEqual(
5278                 seq_encoded[offset:offset + tlvlen],
5279                 obj.encode(),
5280             )
5281             self._assert_expects(obj, expect_outer["expects"])
5282
5283     @given(data_strategy())
5284     def test_default_disappears(self, d):
5285         _schema = list(d.draw(dictionaries(
5286             text_letters(),
5287             sets(integers(), min_size=2, max_size=2),
5288             min_size=1,
5289         )).items())
5290
5291         class Seq(self.base_klass):
5292             schema = [
5293                 (n, Integer(default=d))
5294                 for n, (_, d) in _schema
5295             ]
5296         seq = Seq()
5297         for name, (value, _) in _schema:
5298             seq[name] = Integer(value)
5299         self.assertEqual(len(seq._value), len(_schema))
5300         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
5301         self.assertGreater(len(seq.encode()), len(empty_seq))
5302         for name, (_, default) in _schema:
5303             seq[name] = Integer(default)
5304         self.assertEqual(len(seq._value), 0)
5305         self.assertSequenceEqual(seq.encode(), empty_seq)
5306
5307     @given(data_strategy())
5308     def test_encoded_default_not_accepted(self, d):
5309         _schema = list(d.draw(dictionaries(
5310             text_letters(),
5311             integers(),
5312             min_size=1,
5313         )).items())
5314         tags = [tag_encode(tag) for tag in d.draw(sets(
5315             integers(min_value=0),
5316             min_size=len(_schema),
5317             max_size=len(_schema),
5318         ))]
5319
5320         class SeqWithoutDefault(self.base_klass):
5321             schema = [
5322                 (n, Integer(impl=t))
5323                 for (n, _), t in zip(_schema, tags)
5324             ]
5325         seq_without_default = SeqWithoutDefault()
5326         for name, value in _schema:
5327             seq_without_default[name] = Integer(value)
5328         seq_encoded = seq_without_default.encode()
5329
5330         class SeqWithDefault(self.base_klass):
5331             schema = [
5332                 (n, Integer(default=v, impl=t))
5333                 for (n, v), t in zip(_schema, tags)
5334             ]
5335         seq_with_default = SeqWithDefault()
5336         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5337             seq_with_default.decode(seq_encoded)
5338         for ctx in ({"bered": True}, {"allow_default_values": True}):
5339             seq_decoded, _ = seq_with_default.decode(seq_encoded, ctx=ctx)
5340             self.assertTrue(seq_decoded.ber_encoded)
5341             self.assertTrue(seq_decoded.bered)
5342             seq_decoded = copy(seq_decoded)
5343             self.assertTrue(seq_decoded.ber_encoded)
5344             self.assertTrue(seq_decoded.bered)
5345             for name, value in _schema:
5346                 self.assertEqual(seq_decoded[name], seq_with_default[name])
5347                 self.assertEqual(seq_decoded[name], value)
5348
5349     @given(data_strategy())
5350     def test_missing_from_spec(self, d):
5351         names = list(d.draw(sets(text_letters(), min_size=2)))
5352         tags = [tag_encode(tag) for tag in d.draw(sets(
5353             integers(min_value=0),
5354             min_size=len(names),
5355             max_size=len(names),
5356         ))]
5357         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
5358
5359         class SeqFull(self.base_klass):
5360             schema = [(n, Integer(impl=t)) for n, t in names_tags]
5361         seq_full = SeqFull()
5362         for i, name in enumerate(names):
5363             seq_full[name] = Integer(i)
5364         seq_encoded = seq_full.encode()
5365         altered = names_tags[:-2] + names_tags[-1:]
5366
5367         class SeqMissing(self.base_klass):
5368             schema = [(n, Integer(impl=t)) for n, t in altered]
5369         seq_missing = SeqMissing()
5370         with self.assertRaises(TagMismatch):
5371             seq_missing.decode(seq_encoded)
5372
5373     def test_bered(self):
5374         class Seq(self.base_klass):
5375             schema = (("underlying", Boolean()),)
5376         encoded = Boolean.tag_default + len_encode(1) + b"\x01"
5377         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
5378         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
5379         self.assertFalse(decoded.ber_encoded)
5380         self.assertFalse(decoded.lenindef)
5381         self.assertTrue(decoded.bered)
5382         decoded = copy(decoded)
5383         self.assertFalse(decoded.ber_encoded)
5384         self.assertFalse(decoded.lenindef)
5385         self.assertTrue(decoded.bered)
5386
5387         class Seq(self.base_klass):
5388             schema = (("underlying", OctetString()),)
5389         encoded = (
5390             tag_encode(form=TagFormConstructed, num=4) +
5391             LENINDEF +
5392             OctetString(b"whatever").encode() +
5393             EOC
5394         )
5395         encoded = Seq.tag_default + len_encode(len(encoded)) + encoded
5396         with self.assertRaises(DecodeError):
5397             Seq().decode(encoded)
5398         decoded, _ = Seq().decode(encoded, ctx={"bered": True})
5399         self.assertFalse(decoded.ber_encoded)
5400         self.assertFalse(decoded.lenindef)
5401         self.assertTrue(decoded.bered)
5402         decoded = copy(decoded)
5403         self.assertFalse(decoded.ber_encoded)
5404         self.assertFalse(decoded.lenindef)
5405         self.assertTrue(decoded.bered)
5406
5407
5408 class TestSequence(SeqMixing, CommonMixin, TestCase):
5409     base_klass = Sequence
5410
5411     @given(
5412         integers(),
5413         binary(min_size=1),
5414     )
5415     def test_remaining(self, value, junk):
5416         class Seq(Sequence):
5417             schema = (
5418                 ("whatever", Integer()),
5419             )
5420         int_encoded = Integer(value).encode()
5421         junked = b"".join((
5422             Sequence.tag_default,
5423             len_encode(len(int_encoded + junk)),
5424             int_encoded + junk,
5425         ))
5426         with assertRaisesRegex(self, DecodeError, "remaining"):
5427             Seq().decode(junked)
5428
5429     @given(sets(text_letters(), min_size=2))
5430     def test_obj_unknown(self, names):
5431         missing = names.pop()
5432
5433         class Seq(Sequence):
5434             schema = [(n, Boolean()) for n in names]
5435         seq = Seq()
5436         with self.assertRaises(ObjUnknown) as err:
5437             seq[missing]
5438         repr(err.exception)
5439         with self.assertRaises(ObjUnknown) as err:
5440             seq[missing] = Boolean()
5441         repr(err.exception)
5442
5443     def test_x690_vector(self):
5444         class Seq(Sequence):
5445             schema = (
5446                 ("name", IA5String()),
5447                 ("ok", Boolean()),
5448             )
5449         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
5450         self.assertEqual(seq["name"], "Smith")
5451         self.assertEqual(seq["ok"], True)
5452
5453
5454 class TestSet(SeqMixing, CommonMixin, TestCase):
5455     base_klass = Set
5456
5457     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5458     @given(data_strategy())
5459     def test_sorted(self, d):
5460         tags = [
5461             tag_encode(tag) for tag in
5462             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
5463         ]
5464
5465         class Seq(Set):
5466             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
5467         seq = Seq()
5468         for name, _ in Seq.schema:
5469             seq[name] = OctetString(b"")
5470         seq_encoded = seq.encode()
5471         seq_decoded, _ = seq.decode(seq_encoded)
5472         self.assertSequenceEqual(
5473             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5474             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
5475         )
5476
5477     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5478     @given(data_strategy())
5479     def test_unsorted(self, d):
5480         tags = [
5481             tag_encode(tag) for tag in
5482             d.draw(sets(integers(min_value=1), min_size=2, max_size=5))
5483         ]
5484         tags = d.draw(permutations(tags))
5485         assume(tags != sorted(tags))
5486         encoded = b"".join(OctetString(t, impl=t).encode() for t in tags)
5487         seq_encoded = b"".join((
5488             Set.tag_default,
5489             len_encode(len(encoded)),
5490             encoded,
5491         ))
5492
5493         class Seq(Set):
5494             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
5495         seq = Seq()
5496         with assertRaisesRegex(self, DecodeError, "unordered SET"):
5497             seq.decode(seq_encoded)
5498         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
5499             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
5500             self.assertTrue(seq_decoded.ber_encoded)
5501             self.assertTrue(seq_decoded.bered)
5502             seq_decoded = copy(seq_decoded)
5503             self.assertTrue(seq_decoded.ber_encoded)
5504             self.assertTrue(seq_decoded.bered)
5505             self.assertSequenceEqual(
5506                 [bytes(seq_decoded[str(i)]) for i, t in enumerate(tags)],
5507                 tags,
5508             )
5509
5510
5511 @composite
5512 def seqof_values_strategy(draw, schema=None, do_expl=False):
5513     if schema is None:
5514         schema = draw(sampled_from((Boolean(), Integer())))
5515     bound_min, bound_max = sorted(draw(sets(
5516         integers(min_value=0, max_value=10),
5517         min_size=2,
5518         max_size=2,
5519     )))
5520     if isinstance(schema, Boolean):
5521         values_generator = booleans().map(Boolean)
5522     elif isinstance(schema, Integer):
5523         values_generator = integers().map(Integer)
5524     values_generator = lists(
5525         values_generator,
5526         min_size=bound_min,
5527         max_size=bound_max,
5528     )
5529     values = draw(one_of(none(), values_generator))
5530     impl = None
5531     expl = None
5532     if do_expl:
5533         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5534     else:
5535         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
5536     default = draw(one_of(none(), values_generator))
5537     optional = draw(one_of(none(), booleans()))
5538     _decoded = (
5539         draw(integers(min_value=0)),
5540         draw(integers(min_value=0)),
5541         draw(integers(min_value=0)),
5542     )
5543     return (
5544         schema,
5545         values,
5546         (bound_min, bound_max),
5547         impl,
5548         expl,
5549         default,
5550         optional,
5551         _decoded,
5552     )
5553
5554
5555 class SeqOfMixing(object):
5556     def test_invalid_value_type(self):
5557         with self.assertRaises(InvalidValueType) as err:
5558             self.base_klass(123)
5559         repr(err.exception)
5560
5561     def test_invalid_values_type(self):
5562         class SeqOf(self.base_klass):
5563             schema = Integer()
5564         with self.assertRaises(InvalidValueType) as err:
5565             SeqOf([Integer(123), Boolean(False), Integer(234)])
5566         repr(err.exception)
5567
5568     def test_schema_required(self):
5569         with assertRaisesRegex(self, ValueError, "schema must be specified"):
5570             self.base_klass.__mro__[1]()
5571
5572     @given(booleans(), booleans(), binary(), binary())
5573     def test_comparison(self, value1, value2, tag1, tag2):
5574         class SeqOf(self.base_klass):
5575             schema = Boolean()
5576         obj1 = SeqOf([Boolean(value1)])
5577         obj2 = SeqOf([Boolean(value2)])
5578         self.assertEqual(obj1 == obj2, value1 == value2)
5579         self.assertEqual(obj1 != obj2, value1 != value2)
5580         self.assertEqual(obj1 == list(obj2), value1 == value2)
5581         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
5582         obj1 = SeqOf([Boolean(value1)], impl=tag1)
5583         obj2 = SeqOf([Boolean(value1)], impl=tag2)
5584         self.assertEqual(obj1 == obj2, tag1 == tag2)
5585         self.assertEqual(obj1 != obj2, tag1 != tag2)
5586
5587     @given(lists(booleans()))
5588     def test_iter(self, values):
5589         class SeqOf(self.base_klass):
5590             schema = Boolean()
5591         obj = SeqOf([Boolean(value) for value in values])
5592         self.assertEqual(len(obj), len(values))
5593         for i, value in enumerate(obj):
5594             self.assertEqual(value, values[i])
5595
5596     @given(data_strategy())
5597     def test_ready(self, d):
5598         ready = [Integer(v) for v in d.draw(lists(
5599             integers(),
5600             min_size=1,
5601             max_size=3,
5602         ))]
5603         non_ready = [
5604             Integer() for _ in
5605             range(d.draw(integers(min_value=1, max_value=5)))
5606         ]
5607
5608         class SeqOf(self.base_klass):
5609             schema = Integer()
5610         values = d.draw(permutations(ready + non_ready))
5611         seqof = SeqOf()
5612         for value in values:
5613             seqof.append(value)
5614         self.assertFalse(seqof.ready)
5615         repr(seqof)
5616         list(seqof.pps())
5617         pprint(seqof, big_blobs=True, with_decode_path=True)
5618         with self.assertRaises(ObjNotReady) as err:
5619             seqof.encode()
5620         repr(err.exception)
5621         for i, value in enumerate(values):
5622             self.assertEqual(seqof[i], value)
5623             if not seqof[i].ready:
5624                 seqof[i] = Integer(i)
5625         self.assertTrue(seqof.ready)
5626         repr(seqof)
5627         list(seqof.pps())
5628         pprint(seqof, big_blobs=True, with_decode_path=True)
5629
5630     def test_spec_mismatch(self):
5631         class SeqOf(self.base_klass):
5632             schema = Integer()
5633         seqof = SeqOf()
5634         seqof.append(Integer(123))
5635         with self.assertRaises(ValueError):
5636             seqof.append(Boolean(False))
5637         with self.assertRaises(ValueError):
5638             seqof[0] = Boolean(False)
5639
5640     @given(data_strategy())
5641     def test_bounds_satisfied(self, d):
5642         class SeqOf(self.base_klass):
5643             schema = Boolean()
5644         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
5645         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5646         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
5647         SeqOf(value=value, bounds=(bound_min, bound_max))
5648
5649     @given(data_strategy())
5650     def test_bounds_unsatisfied(self, d):
5651         class SeqOf(self.base_klass):
5652             schema = Boolean()
5653         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
5654         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5655         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
5656         with self.assertRaises(BoundsError) as err:
5657             SeqOf(value=value, bounds=(bound_min, bound_max))
5658         repr(err.exception)
5659         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5660             SeqOf(bounds=(bound_min, bound_max)).decode(
5661                 SeqOf(value).encode()
5662             )
5663         repr(err.exception)
5664         value = [Boolean(True)] * d.draw(integers(
5665             min_value=bound_max + 1,
5666             max_value=bound_max + 10,
5667         ))
5668         with self.assertRaises(BoundsError) as err:
5669             SeqOf(value=value, bounds=(bound_min, bound_max))
5670         repr(err.exception)
5671         with assertRaisesRegex(self, DecodeError, "bounds") as err:
5672             SeqOf(bounds=(bound_min, bound_max)).decode(
5673                 SeqOf(value).encode()
5674             )
5675         repr(err.exception)
5676
5677     @given(integers(min_value=1, max_value=10))
5678     def test_out_of_bounds(self, bound_max):
5679         class SeqOf(self.base_klass):
5680             schema = Integer()
5681             bounds = (0, bound_max)
5682         seqof = SeqOf()
5683         for _ in range(bound_max):
5684             seqof.append(Integer(123))
5685         with self.assertRaises(BoundsError):
5686             seqof.append(Integer(123))
5687
5688     @given(data_strategy())
5689     def test_call(self, d):
5690         (
5691             schema_initial,
5692             value_initial,
5693             bounds_initial,
5694             impl_initial,
5695             expl_initial,
5696             default_initial,
5697             optional_initial,
5698             _decoded_initial,
5699         ) = d.draw(seqof_values_strategy())
5700
5701         class SeqOf(self.base_klass):
5702             schema = schema_initial
5703         obj_initial = SeqOf(
5704             value=value_initial,
5705             bounds=bounds_initial,
5706             impl=impl_initial,
5707             expl=expl_initial,
5708             default=default_initial,
5709             optional=optional_initial or False,
5710             _decoded=_decoded_initial,
5711         )
5712         (
5713             _,
5714             value,
5715             bounds,
5716             impl,
5717             expl,
5718             default,
5719             optional,
5720             _decoded,
5721         ) = d.draw(seqof_values_strategy(
5722             schema=schema_initial,
5723             do_expl=impl_initial is None,
5724         ))
5725         if (default is None) and (obj_initial.default is not None):
5726             bounds = None
5727         if (
5728                 (bounds is None) and
5729                 (value is not None) and
5730                 (bounds_initial is not None) and
5731                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
5732         ):
5733             value = None
5734         if (
5735                 (bounds is None) and
5736                 (default is not None) and
5737                 (bounds_initial is not None) and
5738                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
5739         ):
5740             default = None
5741         obj = obj_initial(
5742             value=value,
5743             bounds=bounds,
5744             impl=impl,
5745             expl=expl,
5746             default=default,
5747             optional=optional,
5748         )
5749         if obj.ready:
5750             value_expected = default if value is None else value
5751             value_expected = (
5752                 default_initial if value_expected is None
5753                 else value_expected
5754             )
5755             value_expected = () if value_expected is None else value_expected
5756             self.assertEqual(obj, value_expected)
5757         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5758         self.assertEqual(obj.expl_tag, expl or expl_initial)
5759         self.assertEqual(
5760             obj.default,
5761             default_initial if default is None else default,
5762         )
5763         if obj.default is None:
5764             optional = optional_initial if optional is None else optional
5765             optional = False if optional is None else optional
5766         else:
5767             optional = True
5768         self.assertEqual(obj.optional, optional)
5769         self.assertEqual(
5770             (obj._bound_min, obj._bound_max),
5771             bounds or bounds_initial or (0, float("+inf")),
5772         )
5773
5774     @given(seqof_values_strategy())
5775     def test_copy(self, values):
5776         _schema, value, bounds, impl, expl, default, optional, _decoded = values
5777
5778         class SeqOf(self.base_klass):
5779             schema = _schema
5780         register_class(SeqOf)
5781         obj = SeqOf(
5782             value=value,
5783             bounds=bounds,
5784             impl=impl,
5785             expl=expl,
5786             default=default,
5787             optional=optional or False,
5788             _decoded=_decoded,
5789         )
5790         for copy_func in copy_funcs:
5791             obj_copied = copy_func(obj)
5792             self.assert_copied_basic_fields(obj, obj_copied)
5793             self.assertEqual(obj._bound_min, obj_copied._bound_min)
5794             self.assertEqual(obj._bound_max, obj_copied._bound_max)
5795             self.assertEqual(obj._value, obj_copied._value)
5796
5797     @given(
5798         lists(binary()),
5799         integers(min_value=1).map(tag_encode),
5800     )
5801     def test_stripped(self, values, tag_impl):
5802         class SeqOf(self.base_klass):
5803             schema = OctetString()
5804         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
5805         with self.assertRaises(NotEnoughData):
5806             obj.decode(obj.encode()[:-1])
5807
5808     @given(
5809         lists(binary()),
5810         integers(min_value=1).map(tag_ctxc),
5811     )
5812     def test_stripped_expl(self, values, tag_expl):
5813         class SeqOf(self.base_klass):
5814             schema = OctetString()
5815         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
5816         with self.assertRaises(NotEnoughData):
5817             obj.decode(obj.encode()[:-1])
5818
5819     @given(
5820         integers(min_value=31),
5821         integers(min_value=0),
5822         decode_path_strat,
5823     )
5824     def test_bad_tag(self, tag, offset, decode_path):
5825         with self.assertRaises(DecodeError) as err:
5826             self.base_klass().decode(
5827                 tag_encode(tag)[:-1],
5828                 offset=offset,
5829                 decode_path=decode_path,
5830             )
5831         repr(err.exception)
5832         self.assertEqual(err.exception.offset, offset)
5833         self.assertEqual(err.exception.decode_path, decode_path)
5834
5835     @given(
5836         integers(min_value=128),
5837         integers(min_value=0),
5838         decode_path_strat,
5839     )
5840     def test_bad_len(self, l, offset, decode_path):
5841         with self.assertRaises(DecodeError) as err:
5842             self.base_klass().decode(
5843                 self.base_klass.tag_default + len_encode(l)[:-1],
5844                 offset=offset,
5845                 decode_path=decode_path,
5846             )
5847         repr(err.exception)
5848         self.assertEqual(err.exception.offset, offset)
5849         self.assertEqual(err.exception.decode_path, decode_path)
5850
5851     @given(binary(min_size=1))
5852     def test_tag_mismatch(self, impl):
5853         assume(impl != self.base_klass.tag_default)
5854         with self.assertRaises(TagMismatch):
5855             self.base_klass(impl=impl).decode(self.base_klass().encode())
5856
5857     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5858     @given(
5859         seqof_values_strategy(schema=Integer()),
5860         lists(integers().map(Integer)),
5861         integers(min_value=1).map(tag_ctxc),
5862         integers(min_value=0),
5863         binary(max_size=5),
5864     )
5865     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
5866         _, _, _, _, _, default, optional, _decoded = values
5867
5868         class SeqOf(self.base_klass):
5869             schema = Integer()
5870         obj = SeqOf(
5871             value=value,
5872             default=default,
5873             optional=optional,
5874             _decoded=_decoded,
5875         )
5876         repr(obj)
5877         list(obj.pps())
5878         pprint(obj, big_blobs=True, with_decode_path=True)
5879         self.assertFalse(obj.expled)
5880         obj_encoded = obj.encode()
5881         obj_expled = obj(value, expl=tag_expl)
5882         self.assertTrue(obj_expled.expled)
5883         repr(obj_expled)
5884         list(obj_expled.pps())
5885         pprint(obj_expled, big_blobs=True, with_decode_path=True)
5886         obj_expled_encoded = obj_expled.encode()
5887         ctx_copied = deepcopy(ctx_dummy)
5888         obj_decoded, tail = obj_expled.decode(
5889             obj_expled_encoded + tail_junk,
5890             offset=offset,
5891             ctx=ctx_copied,
5892         )
5893         self.assertDictEqual(ctx_copied, ctx_dummy)
5894         repr(obj_decoded)
5895         list(obj_decoded.pps())
5896         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
5897         self.assertEqual(tail, tail_junk)
5898         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
5899         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5900         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5901         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5902         self.assertEqual(
5903             obj_decoded.expl_llen,
5904             len(len_encode(len(obj_encoded))),
5905         )
5906         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5907         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5908         self.assertEqual(
5909             obj_decoded.offset,
5910             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5911         )
5912         self.assertEqual(obj_decoded.expl_offset, offset)
5913         for obj_inner in obj_decoded:
5914             self.assertIn(obj_inner, obj_decoded)
5915             self.assertSequenceEqual(
5916                 obj_inner.encode(),
5917                 obj_expled_encoded[
5918                     obj_inner.offset - offset:
5919                     obj_inner.offset + obj_inner.tlvlen - offset
5920                 ],
5921             )
5922
5923         t, _, lv = tag_strip(obj_encoded)
5924         _, _, v = len_decode(lv)
5925         obj_encoded_lenindef = t + LENINDEF + v + EOC
5926         obj_decoded_lenindef, tail_lenindef = obj.decode(
5927             obj_encoded_lenindef + tail_junk,
5928             ctx={"bered": True},
5929         )
5930         self.assertTrue(obj_decoded_lenindef.lenindef)
5931         self.assertTrue(obj_decoded_lenindef.bered)
5932         obj_decoded_lenindef = copy(obj_decoded_lenindef)
5933         self.assertTrue(obj_decoded_lenindef.lenindef)
5934         self.assertTrue(obj_decoded_lenindef.bered)
5935         repr(obj_decoded_lenindef)
5936         list(obj_decoded_lenindef.pps())
5937         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
5938         self.assertEqual(tail_lenindef, tail_junk)
5939         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
5940         with self.assertRaises(DecodeError):
5941             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
5942         with self.assertRaises(DecodeError):
5943             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
5944
5945         assert_exceeding_data(
5946             self,
5947             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
5948             tail_junk,
5949         )
5950
5951     def test_bered(self):
5952         class SeqOf(self.base_klass):
5953             schema = Boolean()
5954         encoded = Boolean(False).encode()
5955         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
5956         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
5957         with self.assertRaises(DecodeError):
5958             SeqOf().decode(encoded)
5959         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
5960         self.assertFalse(decoded.ber_encoded)
5961         self.assertFalse(decoded.lenindef)
5962         self.assertTrue(decoded.bered)
5963         decoded = copy(decoded)
5964         self.assertFalse(decoded.ber_encoded)
5965         self.assertFalse(decoded.lenindef)
5966         self.assertTrue(decoded.bered)
5967
5968         class SeqOf(self.base_klass):
5969             schema = OctetString()
5970         encoded = OctetString(b"whatever").encode()
5971         encoded += (
5972             tag_encode(form=TagFormConstructed, num=4) +
5973             LENINDEF +
5974             OctetString(b"whatever").encode() +
5975             EOC
5976         )
5977         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
5978         with self.assertRaises(DecodeError):
5979             SeqOf().decode(encoded)
5980         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
5981         self.assertFalse(decoded.ber_encoded)
5982         self.assertFalse(decoded.lenindef)
5983         self.assertTrue(decoded.bered)
5984         decoded = copy(decoded)
5985         self.assertFalse(decoded.ber_encoded)
5986         self.assertFalse(decoded.lenindef)
5987         self.assertTrue(decoded.bered)
5988
5989
5990 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
5991     class SeqOf(SequenceOf):
5992         schema = "whatever"
5993     base_klass = SeqOf
5994
5995     def _test_symmetric_compare_objs(self, obj1, obj2):
5996         self.assertEqual(obj1, obj2)
5997         self.assertSequenceEqual(list(obj1), list(obj2))
5998
5999
6000 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
6001     class SeqOf(SetOf):
6002         schema = "whatever"
6003     base_klass = SeqOf
6004
6005     def _test_symmetric_compare_objs(self, obj1, obj2):
6006         self.assertSetEqual(
6007             set(int(v) for v in obj1),
6008             set(int(v) for v in obj2),
6009         )
6010
6011     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6012     @given(data_strategy())
6013     def test_sorted(self, d):
6014         values = [OctetString(v) for v in d.draw(lists(binary()))]
6015
6016         class Seq(SetOf):
6017             schema = OctetString()
6018         seq = Seq(values)
6019         seq_encoded = seq.encode()
6020         seq_decoded, _ = seq.decode(seq_encoded)
6021         self.assertSequenceEqual(
6022             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6023             b"".join(sorted([v.encode() for v in values])),
6024         )
6025
6026     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6027     @given(data_strategy())
6028     def test_unsorted(self, d):
6029         values = [OctetString(v).encode() for v in d.draw(sets(
6030             binary(min_size=1, max_size=5),
6031             min_size=2,
6032             max_size=5,
6033         ))]
6034         values = d.draw(permutations(values))
6035         assume(values != sorted(values))
6036         encoded = b"".join(values)
6037         seq_encoded = b"".join((
6038             SetOf.tag_default,
6039             len_encode(len(encoded)),
6040             encoded,
6041         ))
6042
6043         class Seq(SetOf):
6044             schema = OctetString()
6045         seq = Seq()
6046         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
6047             seq.decode(seq_encoded)
6048
6049         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6050             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6051             self.assertTrue(seq_decoded.ber_encoded)
6052             self.assertTrue(seq_decoded.bered)
6053             seq_decoded = copy(seq_decoded)
6054             self.assertTrue(seq_decoded.ber_encoded)
6055             self.assertTrue(seq_decoded.bered)
6056             self.assertSequenceEqual(
6057                 [obj.encode() for obj in seq_decoded],
6058                 values,
6059             )
6060
6061
6062 class TestGoMarshalVectors(TestCase):
6063     def runTest(self):
6064         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
6065         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
6066         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
6067         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
6068         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
6069
6070         class Seq(Sequence):
6071             schema = (
6072                 ("erste", Integer()),
6073                 ("zweite", Integer(optional=True))
6074             )
6075         seq = Seq()
6076         seq["erste"] = Integer(64)
6077         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6078         seq["erste"] = Integer(0x123456)
6079         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
6080         seq["erste"] = Integer(64)
6081         seq["zweite"] = Integer(65)
6082         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
6083
6084         class NestedSeq(Sequence):
6085             schema = (
6086                 ("nest", Seq()),
6087             )
6088         seq["erste"] = Integer(127)
6089         seq["zweite"] = None
6090         nested = NestedSeq()
6091         nested["nest"] = seq
6092         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
6093
6094         self.assertSequenceEqual(
6095             OctetString(b"\x01\x02\x03").encode(),
6096             hexdec("0403010203"),
6097         )
6098
6099         class Seq(Sequence):
6100             schema = (
6101                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
6102             )
6103         seq = Seq()
6104         seq["erste"] = Integer(64)
6105         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
6106
6107         class Seq(Sequence):
6108             schema = (
6109                 ("erste", Integer(expl=tag_ctxc(5))),
6110             )
6111         seq = Seq()
6112         seq["erste"] = Integer(64)
6113         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
6114
6115         class Seq(Sequence):
6116             schema = (
6117                 ("erste", Null(
6118                     impl=tag_encode(0, klass=TagClassContext),
6119                     optional=True,
6120                 )),
6121             )
6122         seq = Seq()
6123         seq["erste"] = Null()
6124         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
6125         seq["erste"] = None
6126         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6127
6128         self.assertSequenceEqual(
6129             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
6130             hexdec("170d3730303130313030303030305a"),
6131         )
6132         self.assertSequenceEqual(
6133             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
6134             hexdec("170d3039313131353232353631365a"),
6135         )
6136         self.assertSequenceEqual(
6137             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
6138             hexdec("180f32313030303430353132303130315a"),
6139         )
6140
6141         class Seq(Sequence):
6142             schema = (
6143                 ("erste", GeneralizedTime()),
6144             )
6145         seq = Seq()
6146         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
6147         self.assertSequenceEqual(
6148             seq.encode(),
6149             hexdec("3011180f32303039313131353232353631365a"),
6150         )
6151
6152         self.assertSequenceEqual(
6153             BitString((1, b"\x80")).encode(),
6154             hexdec("03020780"),
6155         )
6156         self.assertSequenceEqual(
6157             BitString((12, b"\x81\xF0")).encode(),
6158             hexdec("03030481f0"),
6159         )
6160
6161         self.assertSequenceEqual(
6162             ObjectIdentifier("1.2.3.4").encode(),
6163             hexdec("06032a0304"),
6164         )
6165         self.assertSequenceEqual(
6166             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
6167             hexdec("06092a864888932d010105"),
6168         )
6169         self.assertSequenceEqual(
6170             ObjectIdentifier("2.100.3").encode(),
6171             hexdec("0603813403"),
6172         )
6173
6174         self.assertSequenceEqual(
6175             PrintableString("test").encode(),
6176             hexdec("130474657374"),
6177         )
6178         self.assertSequenceEqual(
6179             PrintableString("x" * 127).encode(),
6180             hexdec("137F" + "78" * 127),
6181         )
6182         self.assertSequenceEqual(
6183             PrintableString("x" * 128).encode(),
6184             hexdec("138180" + "78" * 128),
6185         )
6186         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
6187
6188         class Seq(Sequence):
6189             schema = (
6190                 ("erste", IA5String()),
6191             )
6192         seq = Seq()
6193         seq["erste"] = IA5String("test")
6194         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
6195
6196         class Seq(Sequence):
6197             schema = (
6198                 ("erste", PrintableString()),
6199             )
6200         seq = Seq()
6201         seq["erste"] = PrintableString("test")
6202         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
6203         # Asterisk is actually not allowable
6204         PrintableString._allowable_chars |= set(b"*")
6205         seq["erste"] = PrintableString("test*")
6206         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
6207         PrintableString._allowable_chars -= set(b"*")
6208
6209         class Seq(Sequence):
6210             schema = (
6211                 ("erste", Any(optional=True)),
6212                 ("zweite", Integer()),
6213             )
6214         seq = Seq()
6215         seq["zweite"] = Integer(64)
6216         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6217
6218         class Seq(SetOf):
6219             schema = Integer()
6220         seq = Seq()
6221         seq.append(Integer(10))
6222         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
6223
6224         class _SeqOf(SequenceOf):
6225             schema = PrintableString()
6226
6227         class SeqOf(SequenceOf):
6228             schema = _SeqOf()
6229         _seqof = _SeqOf()
6230         _seqof.append(PrintableString("1"))
6231         seqof = SeqOf()
6232         seqof.append(_seqof)
6233         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
6234
6235         class Seq(Sequence):
6236             schema = (
6237                 ("erste", Integer(default=1)),
6238             )
6239         seq = Seq()
6240         seq["erste"] = Integer(0)
6241         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
6242         seq["erste"] = Integer(1)
6243         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6244         seq["erste"] = Integer(2)
6245         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
6246
6247
6248 class TestPP(TestCase):
6249     @given(data_strategy())
6250     def test_oid_printing(self, d):
6251         oids = {
6252             str(ObjectIdentifier(k)): v * 2
6253             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
6254         }
6255         chosen = d.draw(sampled_from(sorted(oids)))
6256         chosen_id = oids[chosen]
6257         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
6258         self.assertNotIn(chosen_id, pp_console_row(pp))
6259         self.assertIn(
6260             chosen_id,
6261             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
6262         )
6263
6264
6265 class TestAutoAddSlots(TestCase):
6266     def runTest(self):
6267         class Inher(Integer):
6268             pass
6269
6270         with self.assertRaises(AttributeError):
6271             inher = Inher()
6272             inher.unexistent = "whatever"
6273
6274
6275 class TestOIDDefines(TestCase):
6276     @given(data_strategy())
6277     def runTest(self, d):
6278         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
6279         value_name_chosen = d.draw(sampled_from(value_names))
6280         oids = [
6281             ObjectIdentifier(oid)
6282             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
6283         ]
6284         oid_chosen = d.draw(sampled_from(oids))
6285         values = d.draw(lists(
6286             integers(),
6287             min_size=len(value_names),
6288             max_size=len(value_names),
6289         ))
6290         _schema = [
6291             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
6292                 oid: Integer() for oid in oids[:-1]
6293             }),))),
6294         ]
6295         for i, value_name in enumerate(value_names):
6296             _schema.append((value_name, Any(expl=tag_ctxp(i))))
6297
6298         class Seq(Sequence):
6299             schema = _schema
6300         seq = Seq()
6301         for value_name, value in zip(value_names, values):
6302             seq[value_name] = Any(Integer(value).encode())
6303         seq["type"] = oid_chosen
6304         seq, _ = Seq().decode(seq.encode())
6305         for value_name in value_names:
6306             if value_name == value_name_chosen:
6307                 continue
6308             self.assertIsNone(seq[value_name].defined)
6309         if value_name_chosen in oids[:-1]:
6310             self.assertIsNotNone(seq[value_name_chosen].defined)
6311             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
6312             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
6313         repr(seq)
6314         list(seq.pps())
6315         pprint(seq, big_blobs=True, with_decode_path=True)
6316
6317
6318 class TestDefinesByPath(TestCase):
6319     def test_generated(self):
6320         class Seq(Sequence):
6321             schema = (
6322                 ("type", ObjectIdentifier()),
6323                 ("value", OctetString(expl=tag_ctxc(123))),
6324             )
6325
6326         class SeqInner(Sequence):
6327             schema = (
6328                 ("typeInner", ObjectIdentifier()),
6329                 ("valueInner", Any()),
6330             )
6331
6332         class PairValue(SetOf):
6333             schema = Any()
6334
6335         class Pair(Sequence):
6336             schema = (
6337                 ("type", ObjectIdentifier()),
6338                 ("value", PairValue()),
6339             )
6340
6341         class Pairs(SequenceOf):
6342             schema = Pair()
6343
6344         (
6345             type_integered,
6346             type_sequenced,
6347             type_innered,
6348             type_octet_stringed,
6349         ) = [
6350             ObjectIdentifier(oid)
6351             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
6352         ]
6353         seq_integered = Seq()
6354         seq_integered["type"] = type_integered
6355         seq_integered["value"] = OctetString(Integer(123).encode())
6356         seq_integered_raw = seq_integered.encode()
6357
6358         pairs = Pairs()
6359         pairs_input = (
6360             (type_octet_stringed, OctetString(b"whatever")),
6361             (type_integered, Integer(123)),
6362             (type_octet_stringed, OctetString(b"whenever")),
6363             (type_integered, Integer(234)),
6364         )
6365         for t, v in pairs_input:
6366             pair = Pair()
6367             pair["type"] = t
6368             pair["value"] = PairValue((Any(v),))
6369             pairs.append(pair)
6370         seq_inner = SeqInner()
6371         seq_inner["typeInner"] = type_innered
6372         seq_inner["valueInner"] = Any(pairs)
6373         seq_sequenced = Seq()
6374         seq_sequenced["type"] = type_sequenced
6375         seq_sequenced["value"] = OctetString(seq_inner.encode())
6376         seq_sequenced_raw = seq_sequenced.encode()
6377         repr(seq_sequenced)
6378         list(seq_sequenced.pps())
6379         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6380
6381         defines_by_path = []
6382         ctx_copied = deepcopy(ctx_dummy)
6383         seq_integered, _ = Seq().decode(
6384             seq_integered_raw,
6385             ctx=ctx_copied,
6386         )
6387         self.assertDictEqual(ctx_copied, ctx_dummy)
6388         self.assertIsNone(seq_integered["value"].defined)
6389         defines_by_path.append(
6390             (("type",), ((("value",), {
6391                 type_integered: Integer(),
6392                 type_sequenced: SeqInner(),
6393             }),))
6394         )
6395         ctx_copied["defines_by_path"] = defines_by_path
6396         seq_integered, _ = Seq().decode(
6397             seq_integered_raw,
6398             ctx=ctx_copied,
6399         )
6400         del ctx_copied["defines_by_path"]
6401         self.assertDictEqual(ctx_copied, ctx_dummy)
6402         self.assertIsNotNone(seq_integered["value"].defined)
6403         self.assertEqual(seq_integered["value"].defined[0], type_integered)
6404         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
6405         self.assertTrue(seq_integered_raw[
6406             seq_integered["value"].defined[1].offset:
6407         ].startswith(Integer(123).encode()))
6408         repr(seq_integered)
6409         list(seq_integered.pps())
6410         pprint(seq_integered, big_blobs=True, with_decode_path=True)
6411
6412         ctx_copied["defines_by_path"] = defines_by_path
6413         seq_sequenced, _ = Seq().decode(
6414             seq_sequenced_raw,
6415             ctx=ctx_copied,
6416         )
6417         del ctx_copied["defines_by_path"]
6418         self.assertDictEqual(ctx_copied, ctx_dummy)
6419         self.assertIsNotNone(seq_sequenced["value"].defined)
6420         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6421         seq_inner = seq_sequenced["value"].defined[1]
6422         self.assertIsNone(seq_inner["valueInner"].defined)
6423         repr(seq_sequenced)
6424         list(seq_sequenced.pps())
6425         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6426
6427         defines_by_path.append((
6428             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
6429             ((("valueInner",), {type_innered: Pairs()}),),
6430         ))
6431         ctx_copied["defines_by_path"] = defines_by_path
6432         seq_sequenced, _ = Seq().decode(
6433             seq_sequenced_raw,
6434             ctx=ctx_copied,
6435         )
6436         del ctx_copied["defines_by_path"]
6437         self.assertDictEqual(ctx_copied, ctx_dummy)
6438         self.assertIsNotNone(seq_sequenced["value"].defined)
6439         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6440         seq_inner = seq_sequenced["value"].defined[1]
6441         self.assertIsNotNone(seq_inner["valueInner"].defined)
6442         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
6443         pairs = seq_inner["valueInner"].defined[1]
6444         for pair in pairs:
6445             self.assertIsNone(pair["value"][0].defined)
6446         repr(seq_sequenced)
6447         list(seq_sequenced.pps())
6448         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6449
6450         defines_by_path.append((
6451             (
6452                 "value",
6453                 DecodePathDefBy(type_sequenced),
6454                 "valueInner",
6455                 DecodePathDefBy(type_innered),
6456                 any,
6457                 "type",
6458             ),
6459             ((("value",), {
6460                 type_integered: Integer(),
6461                 type_octet_stringed: OctetString(),
6462             }),),
6463         ))
6464         ctx_copied["defines_by_path"] = defines_by_path
6465         seq_sequenced, _ = Seq().decode(
6466             seq_sequenced_raw,
6467             ctx=ctx_copied,
6468         )
6469         del ctx_copied["defines_by_path"]
6470         self.assertDictEqual(ctx_copied, ctx_dummy)
6471         self.assertIsNotNone(seq_sequenced["value"].defined)
6472         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6473         seq_inner = seq_sequenced["value"].defined[1]
6474         self.assertIsNotNone(seq_inner["valueInner"].defined)
6475         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
6476         pairs_got = seq_inner["valueInner"].defined[1]
6477         for pair_input, pair_got in zip(pairs_input, pairs_got):
6478             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
6479             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
6480         repr(seq_sequenced)
6481         list(seq_sequenced.pps())
6482         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6483
6484     @given(oid_strategy(), integers())
6485     def test_simple(self, oid, tgt):
6486         class Inner(Sequence):
6487             schema = (
6488                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
6489                     ObjectIdentifier(oid): Integer(),
6490                 }),))),
6491             )
6492
6493         class Outer(Sequence):
6494             schema = (
6495                 ("inner", Inner()),
6496                 ("tgt", OctetString()),
6497             )
6498
6499         inner = Inner()
6500         inner["oid"] = ObjectIdentifier(oid)
6501         outer = Outer()
6502         outer["inner"] = inner
6503         outer["tgt"] = OctetString(Integer(tgt).encode())
6504         decoded, _ = Outer().decode(outer.encode())
6505         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
6506
6507
6508 class TestAbsDecodePath(TestCase):
6509     @given(
6510         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
6511         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
6512     )
6513     def test_concat(self, decode_path, rel_path):
6514         self.assertSequenceEqual(
6515             abs_decode_path(decode_path, rel_path),
6516             decode_path + rel_path,
6517         )
6518
6519     @given(
6520         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
6521         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
6522     )
6523     def test_abs(self, decode_path, rel_path):
6524         self.assertSequenceEqual(
6525             abs_decode_path(decode_path, ("/",) + rel_path),
6526             rel_path,
6527         )
6528
6529     @given(
6530         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
6531         integers(min_value=1, max_value=3),
6532         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
6533     )
6534     def test_dots(self, decode_path, number_of_dots, rel_path):
6535         self.assertSequenceEqual(
6536             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
6537             decode_path[:-number_of_dots] + rel_path,
6538         )
6539
6540
6541 class TestStrictDefaultExistence(TestCase):
6542     @given(data_strategy())
6543     def runTest(self, d):
6544         count = d.draw(integers(min_value=1, max_value=10))
6545         chosen = d.draw(integers(min_value=0, max_value=count - 1))
6546         _schema = [
6547             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
6548             for i in range(count)
6549         ]
6550         for klass in (Sequence, Set):
6551             class Seq(klass):
6552                 schema = _schema
6553             seq = Seq()
6554             for i in range(count):
6555                 seq["int%d" % i] = Integer(123)
6556             raw = seq.encode()
6557             chosen_choice = "int%d" % chosen
6558             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
6559             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
6560                 seq.decode(raw)
6561             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
6562             self.assertTrue(decoded.ber_encoded)
6563             self.assertTrue(decoded.bered)
6564             decoded = copy(decoded)
6565             self.assertTrue(decoded.ber_encoded)
6566             self.assertTrue(decoded.bered)
6567             decoded, _ = seq.decode(raw, ctx={"bered": True})
6568             self.assertTrue(decoded.ber_encoded)
6569             self.assertTrue(decoded.bered)
6570             decoded = copy(decoded)
6571             self.assertTrue(decoded.ber_encoded)
6572             self.assertTrue(decoded.bered)
6573
6574
6575 class TestX690PrefixedType(TestCase):
6576     def runTest(self):
6577         self.assertSequenceEqual(
6578             VisibleString("Jones").encode(),
6579             hexdec("1A054A6F6E6573"),
6580         )
6581         self.assertSequenceEqual(
6582             VisibleString(
6583                 "Jones",
6584                 impl=tag_encode(3, klass=TagClassApplication),
6585             ).encode(),
6586             hexdec("43054A6F6E6573"),
6587         )
6588         self.assertSequenceEqual(
6589             Any(
6590                 VisibleString(
6591                     "Jones",
6592                     impl=tag_encode(3, klass=TagClassApplication),
6593                 ),
6594                 expl=tag_ctxc(2),
6595             ).encode(),
6596             hexdec("A20743054A6F6E6573"),
6597         )
6598         self.assertSequenceEqual(
6599             OctetString(
6600                 VisibleString(
6601                     "Jones",
6602                     impl=tag_encode(3, klass=TagClassApplication),
6603                 ).encode(),
6604                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
6605             ).encode(),
6606             hexdec("670743054A6F6E6573"),
6607         )
6608         self.assertSequenceEqual(
6609             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
6610             hexdec("82054A6F6E6573"),
6611         )
6612
6613
6614 class TestExplOOB(TestCase):
6615     def runTest(self):
6616         expl = tag_ctxc(123)
6617         raw = Integer(123).encode() + Integer(234).encode()
6618         raw = b"".join((expl, len_encode(len(raw)), raw))
6619         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
6620             Integer(expl=expl).decode(raw)
6621         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
6622
6623
6624 class TestPickleDifferentVersion(TestCase):
6625     def runTest(self):
6626         pickled = pickle_dumps(Integer(123), pickle_proto)
6627         import pyderasn
6628         version_orig = pyderasn.__version__
6629         pyderasn.__version__ += "different"
6630         with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
6631             pickle_loads(pickled)
6632         pyderasn.__version__ = version_orig
6633         pickle_loads(pickled)