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