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