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