13322d7b454679e13d5f9e97d40fb9313729a12e
[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 printable
22 from string import whitespace
23 from unittest import TestCase
24
25 from hypothesis import assume
26 from hypothesis import given
27 from hypothesis import settings
28 from hypothesis.strategies import binary
29 from hypothesis.strategies import booleans
30 from hypothesis.strategies import composite
31 from hypothesis.strategies import data as data_strategy
32 from hypothesis.strategies import datetimes
33 from hypothesis.strategies import dictionaries
34 from hypothesis.strategies import integers
35 from hypothesis.strategies import just
36 from hypothesis.strategies import lists
37 from hypothesis.strategies import none
38 from hypothesis.strategies import one_of
39 from hypothesis.strategies import permutations
40 from hypothesis.strategies import sampled_from
41 from hypothesis.strategies import sets
42 from hypothesis.strategies import text
43 from hypothesis.strategies import tuples
44 from six import assertRaisesRegex
45 from six import byte2int
46 from six import indexbytes
47 from six import int2byte
48 from six import iterbytes
49 from six import PY2
50 from six import text_type
51
52 from pyderasn import _pp
53 from pyderasn import abs_decode_path
54 from pyderasn import Any
55 from pyderasn import BitString
56 from pyderasn import BMPString
57 from pyderasn import Boolean
58 from pyderasn import BoundsError
59 from pyderasn import Choice
60 from pyderasn import decode_path_defby
61 from pyderasn import DecodeError
62 from pyderasn import Enumerated
63 from pyderasn import GeneralizedTime
64 from pyderasn import GeneralString
65 from pyderasn import GraphicString
66 from pyderasn import hexdec
67 from pyderasn import hexenc
68 from pyderasn import IA5String
69 from pyderasn import Integer
70 from pyderasn import InvalidLength
71 from pyderasn import InvalidOID
72 from pyderasn import InvalidValueType
73 from pyderasn import len_decode
74 from pyderasn import len_encode
75 from pyderasn import NotEnoughData
76 from pyderasn import Null
77 from pyderasn import NumericString
78 from pyderasn import ObjectIdentifier
79 from pyderasn import ObjNotReady
80 from pyderasn import ObjUnknown
81 from pyderasn import OctetString
82 from pyderasn import pp_console_row
83 from pyderasn import pprint
84 from pyderasn import PrintableString
85 from pyderasn import Sequence
86 from pyderasn import SequenceOf
87 from pyderasn import Set
88 from pyderasn import SetOf
89 from pyderasn import tag_ctxc
90 from pyderasn import tag_ctxp
91 from pyderasn import tag_decode
92 from pyderasn import tag_encode
93 from pyderasn import tag_strip
94 from pyderasn import TagClassApplication
95 from pyderasn import TagClassContext
96 from pyderasn import TagClassPrivate
97 from pyderasn import TagClassUniversal
98 from pyderasn import TagFormConstructed
99 from pyderasn import TagFormPrimitive
100 from pyderasn import TagMismatch
101 from pyderasn import TeletexString
102 from pyderasn import UniversalString
103 from pyderasn import UTCTime
104 from pyderasn import UTF8String
105 from pyderasn import VideotexString
106 from pyderasn import VisibleString
107
108
109 settings.register_profile('local', settings(
110     deadline=5000,
111     perform_health_check=False,
112 ))
113 settings.load_profile('local')
114 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
115
116 tag_classes = sampled_from((
117     TagClassApplication,
118     TagClassContext,
119     TagClassPrivate,
120     TagClassUniversal,
121 ))
122 tag_forms = sampled_from((TagFormConstructed, TagFormPrimitive))
123
124
125 class TestHex(TestCase):
126     @given(binary())
127     def test_symmetric(self, data):
128         self.assertEqual(hexdec(hexenc(data)), data)
129
130
131 class TestTagCoder(TestCase):
132     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
133     @given(
134         tag_classes,
135         tag_forms,
136         integers(min_value=0, max_value=30),
137         binary(max_size=5),
138     )
139     def test_short(self, klass, form, num, junk):
140         raw = tag_encode(klass=klass, form=form, num=num)
141         self.assertEqual(tag_decode(raw), (klass, form, num))
142         self.assertEqual(len(raw), 1)
143         self.assertEqual(
144             byte2int(tag_encode(klass=klass, form=form, num=0)),
145             byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
146         )
147         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
148         self.assertSequenceEqual(stripped.tobytes(), raw)
149         self.assertEqual(tlen, len(raw))
150         self.assertSequenceEqual(tail, junk)
151
152     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
153     @given(
154         tag_classes,
155         tag_forms,
156         integers(min_value=31),
157         binary(max_size=5),
158     )
159     def test_long(self, klass, form, num, junk):
160         raw = tag_encode(klass=klass, form=form, num=num)
161         self.assertEqual(tag_decode(raw), (klass, form, num))
162         self.assertGreater(len(raw), 1)
163         self.assertEqual(
164             byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
165             byte2int(raw[:1]),
166         )
167         self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
168         self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
169         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
170         self.assertSequenceEqual(stripped.tobytes(), raw)
171         self.assertEqual(tlen, len(raw))
172         self.assertSequenceEqual(tail, junk)
173
174     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
175     @given(integers(min_value=31))
176     def test_unfinished_tag(self, num):
177         raw = bytearray(tag_encode(num=num))
178         for i in range(1, len(raw)):
179             raw[i] |= 0x80
180         with assertRaisesRegex(self, DecodeError, "unfinished tag"):
181             tag_strip(bytes(raw))
182
183     def test_go_vectors_valid(self):
184         for data, (eklass, etag, elen, eform) in (
185                 (b"\x80\x01", (TagClassContext, 0, 1, TagFormPrimitive)),
186                 (b"\xa0\x01", (TagClassContext, 0, 1, TagFormConstructed)),
187                 (b"\x02\x00", (TagClassUniversal, 2, 0, TagFormPrimitive)),
188                 (b"\xfe\x00", (TagClassPrivate, 30, 0, TagFormConstructed)),
189                 (b"\x1f\x1f\x00", (TagClassUniversal, 31, 0, TagFormPrimitive)),
190                 (b"\x1f\x81\x00\x00", (TagClassUniversal, 128, 0, TagFormPrimitive)),
191                 (b"\x1f\x81\x80\x01\x00", (TagClassUniversal, 0x4001, 0, TagFormPrimitive)),
192                 (b"\x00\x81\x80", (TagClassUniversal, 0, 128, TagFormPrimitive)),
193                 (b"\x00\x82\x01\x00", (TagClassUniversal, 0, 256, TagFormPrimitive)),
194                 (b"\xa0\x84\x7f\xff\xff\xff", (TagClassContext, 0, 0x7fffffff, TagFormConstructed)),
195         ):
196             tag, _, len_encoded = tag_strip(memoryview(data))
197             klass, form, num = tag_decode(tag)
198             _len, _, tail = len_decode(len_encoded)
199             self.assertSequenceEqual(tail, b"")
200             self.assertEqual(klass, eklass)
201             self.assertEqual(num, etag)
202             self.assertEqual(_len, elen)
203             self.assertEqual(form, eform)
204
205     def test_go_vectors_invalid(self):
206         for data in (
207                 b"\x00\x83\x01\x00",
208                 b"\x1f\x85",
209                 b"\x30\x80",
210                 b"\xa0\x82\x00\xff",
211                 b"\xa0\x81\x7f",
212         ):
213             with self.assertRaises(DecodeError):
214                 _, _, len_encoded = tag_strip(memoryview(data))
215                 len_decode(len_encoded)
216
217     @given(
218         integers(min_value=0, max_value=127),
219         integers(min_value=0, max_value=2),
220     )
221     def test_long_instead_of_short(self, l, dummy_num):
222         octets = (b"\x00" * dummy_num) + int2byte(l)
223         octets = int2byte((dummy_num + 1) | 0x80) + octets
224         with self.assertRaises(DecodeError):
225             len_decode(octets)
226
227
228 class TestLenCoder(TestCase):
229     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
230     @given(
231         integers(min_value=0, max_value=127),
232         binary(max_size=5),
233     )
234     def test_short(self, l, junk):
235         raw = len_encode(l) + junk
236         decoded, llen, tail = len_decode(memoryview(raw))
237         self.assertEqual(decoded, l)
238         self.assertEqual(llen, 1)
239         self.assertEqual(len(raw), 1 + len(junk))
240         self.assertEqual(tail.tobytes(), junk)
241
242     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
243     @given(
244         integers(min_value=128),
245         binary(max_size=5),
246     )
247     def test_long(self, l, junk):
248         raw = len_encode(l) + junk
249         decoded, llen, tail = len_decode(memoryview(raw))
250         self.assertEqual(decoded, l)
251         self.assertEqual((llen - 1) | 0x80, byte2int(raw))
252         self.assertEqual(llen, len(raw) - len(junk))
253         self.assertNotEqual(indexbytes(raw, 1), 0)
254         self.assertSequenceEqual(tail.tobytes(), junk)
255
256     def test_empty(self):
257         with self.assertRaises(NotEnoughData):
258             len_decode(b"")
259
260     @given(integers(min_value=128))
261     def test_stripped(self, _len):
262         with self.assertRaises(NotEnoughData):
263             len_decode(len_encode(_len)[:-1])
264
265
266 text_printable = text(alphabet=printable, min_size=1)
267
268
269 @composite
270 def text_letters(draw):
271     result = draw(text(alphabet=ascii_letters, min_size=1))
272     if PY2:
273         result = result.encode("ascii")
274     return result
275
276
277 class CommonMixin(object):
278     def test_tag_default(self):
279         obj = self.base_klass()
280         self.assertEqual(obj.tag, obj.tag_default)
281
282     def test_simultaneous_impl_expl(self):
283         with self.assertRaises(ValueError):
284             self.base_klass(impl=b"whatever", expl=b"whenever")
285
286     @given(binary(), integers(), integers(), integers())
287     def test_decoded(self, impl, offset, llen, vlen):
288         obj = self.base_klass(impl=impl, _decoded=(offset, llen, vlen))
289         self.assertEqual(obj.offset, offset)
290         self.assertEqual(obj.llen, llen)
291         self.assertEqual(obj.vlen, vlen)
292         self.assertEqual(obj.tlen, len(impl))
293         self.assertEqual(obj.tlvlen, obj.tlen + obj.llen + obj.vlen)
294
295     @given(binary())
296     def test_impl_inherited(self, impl_tag):
297         class Inherited(self.base_klass):
298             impl = impl_tag
299         obj = Inherited()
300         self.assertSequenceEqual(obj.impl, impl_tag)
301         self.assertFalse(obj.expled)
302
303     @given(binary())
304     def test_expl_inherited(self, expl_tag):
305         class Inherited(self.base_klass):
306             expl = expl_tag
307         obj = Inherited()
308         self.assertSequenceEqual(obj.expl, expl_tag)
309         self.assertTrue(obj.expled)
310
311     def assert_copied_basic_fields(self, obj, obj_copied):
312         self.assertEqual(obj, obj_copied)
313         self.assertSequenceEqual(obj.tag, obj_copied.tag)
314         self.assertEqual(obj.expl_tag, obj_copied.expl_tag)
315         self.assertEqual(obj.default, obj_copied.default)
316         self.assertEqual(obj.optional, obj_copied.optional)
317         self.assertEqual(obj.offset, obj_copied.offset)
318         self.assertEqual(obj.llen, obj_copied.llen)
319         self.assertEqual(obj.vlen, obj_copied.vlen)
320
321
322 @composite
323 def boolean_values_strategy(draw, do_expl=False):
324     value = draw(one_of(none(), booleans()))
325     impl = None
326     expl = None
327     if do_expl:
328         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
329     else:
330         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
331     default = draw(one_of(none(), booleans()))
332     optional = draw(one_of(none(), booleans()))
333     _decoded = (
334         draw(integers(min_value=0)),
335         draw(integers(min_value=0)),
336         draw(integers(min_value=0)),
337     )
338     return (value, impl, expl, default, optional, _decoded)
339
340
341 class BooleanInherited(Boolean):
342     pass
343
344
345 class TestBoolean(CommonMixin, TestCase):
346     base_klass = Boolean
347
348     def test_invalid_value_type(self):
349         with self.assertRaises(InvalidValueType) as err:
350             Boolean(123)
351         repr(err.exception)
352
353     @given(booleans())
354     def test_optional(self, optional):
355         obj = Boolean(default=Boolean(False), optional=optional)
356         self.assertTrue(obj.optional)
357
358     @given(booleans())
359     def test_ready(self, value):
360         obj = Boolean()
361         self.assertFalse(obj.ready)
362         repr(obj)
363         pprint(obj)
364         with self.assertRaises(ObjNotReady) as err:
365             obj.encode()
366         repr(err.exception)
367         obj = Boolean(value)
368         self.assertTrue(obj.ready)
369         repr(obj)
370         pprint(obj)
371
372     @given(booleans(), booleans(), binary(), binary())
373     def test_comparison(self, value1, value2, tag1, tag2):
374         for klass in (Boolean, BooleanInherited):
375             obj1 = klass(value1)
376             obj2 = klass(value2)
377             self.assertEqual(obj1 == obj2, value1 == value2)
378             self.assertEqual(obj1 != obj2, value1 != value2)
379             self.assertEqual(obj1 == bool(obj2), value1 == value2)
380             obj1 = klass(value1, impl=tag1)
381             obj2 = klass(value1, impl=tag2)
382             self.assertEqual(obj1 == obj2, tag1 == tag2)
383             self.assertEqual(obj1 != obj2, tag1 != tag2)
384
385     @given(data_strategy())
386     def test_call(self, d):
387         for klass in (Boolean, BooleanInherited):
388             (
389                 value_initial,
390                 impl_initial,
391                 expl_initial,
392                 default_initial,
393                 optional_initial,
394                 _decoded_initial,
395             ) = d.draw(boolean_values_strategy())
396             obj_initial = klass(
397                 value_initial,
398                 impl_initial,
399                 expl_initial,
400                 default_initial,
401                 optional_initial or False,
402                 _decoded_initial,
403             )
404             (
405                 value,
406                 impl,
407                 expl,
408                 default,
409                 optional,
410                 _decoded,
411             ) = d.draw(boolean_values_strategy(do_expl=impl_initial is None))
412             obj = obj_initial(value, impl, expl, default, optional)
413             if obj.ready:
414                 value_expected = default if value is None else value
415                 value_expected = (
416                     default_initial if value_expected is None
417                     else value_expected
418                 )
419                 self.assertEqual(obj, value_expected)
420             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
421             self.assertEqual(obj.expl_tag, expl or expl_initial)
422             self.assertEqual(
423                 obj.default,
424                 default_initial if default is None else default,
425             )
426             if obj.default is None:
427                 optional = optional_initial if optional is None else optional
428                 optional = False if optional is None else optional
429             else:
430                 optional = True
431             self.assertEqual(obj.optional, optional)
432
433     @given(boolean_values_strategy())
434     def test_copy(self, values):
435         for klass in (Boolean, BooleanInherited):
436             obj = klass(*values)
437             obj_copied = obj.copy()
438             self.assert_copied_basic_fields(obj, obj_copied)
439
440     @given(
441         booleans(),
442         integers(min_value=1).map(tag_encode),
443     )
444     def test_stripped(self, value, tag_impl):
445         obj = Boolean(value, impl=tag_impl)
446         with self.assertRaises(NotEnoughData):
447             obj.decode(obj.encode()[:-1])
448
449     @given(
450         booleans(),
451         integers(min_value=1).map(tag_ctxc),
452     )
453     def test_stripped_expl(self, value, tag_expl):
454         obj = Boolean(value, expl=tag_expl)
455         with self.assertRaises(NotEnoughData):
456             obj.decode(obj.encode()[:-1])
457
458     @given(
459         integers(min_value=31),
460         integers(min_value=0),
461         lists(integers()),
462     )
463     def test_bad_tag(self, tag, offset, decode_path):
464         decode_path = tuple(str(i) for i in decode_path)
465         with self.assertRaises(DecodeError) as err:
466             Boolean().decode(
467                 tag_encode(tag)[:-1],
468                 offset=offset,
469                 decode_path=decode_path,
470             )
471         repr(err.exception)
472         self.assertEqual(err.exception.offset, offset)
473         self.assertEqual(err.exception.decode_path, decode_path)
474
475     @given(
476         integers(min_value=31),
477         integers(min_value=0),
478         lists(integers()),
479     )
480     def test_bad_expl_tag(self, tag, offset, decode_path):
481         decode_path = tuple(str(i) for i in decode_path)
482         with self.assertRaises(DecodeError) as err:
483             Boolean(expl=Boolean.tag_default).decode(
484                 tag_encode(tag)[:-1],
485                 offset=offset,
486                 decode_path=decode_path,
487             )
488         repr(err.exception)
489         self.assertEqual(err.exception.offset, offset)
490         self.assertEqual(err.exception.decode_path, decode_path)
491
492     @given(
493         integers(min_value=128),
494         integers(min_value=0),
495         lists(integers()),
496     )
497     def test_bad_len(self, l, offset, decode_path):
498         decode_path = tuple(str(i) for i in decode_path)
499         with self.assertRaises(DecodeError) as err:
500             Boolean().decode(
501                 Boolean.tag_default + len_encode(l)[:-1],
502                 offset=offset,
503                 decode_path=decode_path,
504             )
505         repr(err.exception)
506         self.assertEqual(err.exception.offset, offset)
507         self.assertEqual(err.exception.decode_path, decode_path)
508
509     @given(
510         integers(min_value=128),
511         integers(min_value=0),
512         lists(integers()),
513     )
514     def test_bad_expl_len(self, l, offset, decode_path):
515         decode_path = tuple(str(i) for i in decode_path)
516         with self.assertRaises(DecodeError) as err:
517             Boolean(expl=Boolean.tag_default).decode(
518                 Boolean.tag_default + len_encode(l)[:-1],
519                 offset=offset,
520                 decode_path=decode_path,
521             )
522         repr(err.exception)
523         self.assertEqual(err.exception.offset, offset)
524         self.assertEqual(err.exception.decode_path, decode_path)
525
526     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
527     @given(
528         boolean_values_strategy(),
529         booleans(),
530         integers(min_value=1).map(tag_ctxc),
531         integers(min_value=0),
532     )
533     def test_symmetric(self, values, value, tag_expl, offset):
534         for klass in (Boolean, BooleanInherited):
535             _, _, _, default, optional, _decoded = values
536             obj = klass(
537                 value=value,
538                 default=default,
539                 optional=optional,
540                 _decoded=_decoded,
541             )
542             repr(obj)
543             pprint(obj)
544             self.assertFalse(obj.expled)
545             obj_encoded = obj.encode()
546             obj_expled = obj(value, expl=tag_expl)
547             self.assertTrue(obj_expled.expled)
548             repr(obj_expled)
549             pprint(obj_expled)
550             obj_expled_encoded = obj_expled.encode()
551             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
552             repr(obj_decoded)
553             pprint(obj_decoded)
554             self.assertEqual(tail, b"")
555             self.assertEqual(obj_decoded, obj_expled)
556             self.assertNotEqual(obj_decoded, obj)
557             self.assertEqual(bool(obj_decoded), bool(obj_expled))
558             self.assertEqual(bool(obj_decoded), bool(obj))
559             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
560             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
561             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
562             self.assertEqual(
563                 obj_decoded.expl_llen,
564                 len(len_encode(len(obj_encoded))),
565             )
566             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
567             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
568             self.assertEqual(
569                 obj_decoded.offset,
570                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
571             )
572             self.assertEqual(obj_decoded.expl_offset, offset)
573
574     @given(integers(min_value=2))
575     def test_invalid_len(self, l):
576         with self.assertRaises(InvalidLength):
577             Boolean().decode(b"".join((
578                 Boolean.tag_default,
579                 len_encode(l),
580                 b"\x00" * l,
581             )))
582
583     @given(integers(min_value=0 + 1, max_value=255 - 1))
584     def test_invalid_value(self, value):
585         with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
586             Boolean().decode(b"".join((
587                 Boolean.tag_default,
588                 len_encode(1),
589                 int2byte(value),
590             )))
591
592
593 @composite
594 def integer_values_strategy(draw, do_expl=False):
595     bound_min, value, default, bound_max = sorted(draw(sets(
596         integers(),
597         min_size=4,
598         max_size=4,
599     )))
600     if draw(booleans()):
601         value = None
602     _specs = None
603     if draw(booleans()):
604         _specs = draw(sets(text_letters()))
605         values = draw(sets(
606             integers(),
607             min_size=len(_specs),
608             max_size=len(_specs),
609         ))
610         _specs = list(zip(_specs, values))
611     bounds = None
612     if draw(booleans()):
613         bounds = (bound_min, bound_max)
614     impl = None
615     expl = None
616     if do_expl:
617         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
618     else:
619         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
620     if draw(booleans()):
621         default = None
622     optional = draw(one_of(none(), booleans()))
623     _decoded = (
624         draw(integers(min_value=0)),
625         draw(integers(min_value=0)),
626         draw(integers(min_value=0)),
627     )
628     return (value, bounds, impl, expl, default, optional, _specs, _decoded)
629
630
631 class IntegerInherited(Integer):
632     pass
633
634
635 class TestInteger(CommonMixin, TestCase):
636     base_klass = Integer
637
638     def test_invalid_value_type(self):
639         with self.assertRaises(InvalidValueType) as err:
640             Integer(12.3)
641         repr(err.exception)
642
643     @given(sets(text_letters(), min_size=2))
644     def test_unknown_name(self, names_input):
645         missing = names_input.pop()
646
647         class Int(Integer):
648             schema = [(n, 123) for n in names_input]
649         with self.assertRaises(ObjUnknown) as err:
650             Int(missing)
651         repr(err.exception)
652
653     @given(sets(text_letters(), min_size=2))
654     def test_known_name(self, names_input):
655         class Int(Integer):
656             schema = [(n, 123) for n in names_input]
657         Int(names_input.pop())
658
659     @given(booleans())
660     def test_optional(self, optional):
661         obj = Integer(default=Integer(0), optional=optional)
662         self.assertTrue(obj.optional)
663
664     @given(integers())
665     def test_ready(self, value):
666         obj = Integer()
667         self.assertFalse(obj.ready)
668         repr(obj)
669         pprint(obj)
670         with self.assertRaises(ObjNotReady) as err:
671             obj.encode()
672         repr(err.exception)
673         obj = Integer(value)
674         self.assertTrue(obj.ready)
675         repr(obj)
676         pprint(obj)
677         hash(obj)
678
679     @given(integers(), integers(), binary(), binary())
680     def test_comparison(self, value1, value2, tag1, tag2):
681         for klass in (Integer, IntegerInherited):
682             obj1 = klass(value1)
683             obj2 = klass(value2)
684             self.assertEqual(obj1 == obj2, value1 == value2)
685             self.assertEqual(obj1 != obj2, value1 != value2)
686             self.assertEqual(obj1 == int(obj2), value1 == value2)
687             obj1 = klass(value1, impl=tag1)
688             obj2 = klass(value1, impl=tag2)
689             self.assertEqual(obj1 == obj2, tag1 == tag2)
690             self.assertEqual(obj1 != obj2, tag1 != tag2)
691
692     @given(lists(integers()))
693     def test_sorted_works(self, values):
694         self.assertSequenceEqual(
695             [int(v) for v in sorted(Integer(v) for v in values)],
696             sorted(values),
697         )
698
699     @given(data_strategy())
700     def test_named(self, d):
701         names_input = list(d.draw(sets(text_letters(), min_size=1)))
702         values_input = list(d.draw(sets(
703             integers(),
704             min_size=len(names_input),
705             max_size=len(names_input),
706         )))
707         chosen_name = d.draw(sampled_from(names_input))
708         names_input = dict(zip(names_input, values_input))
709
710         class Int(Integer):
711             schema = names_input
712         _int = Int(chosen_name)
713         self.assertEqual(_int.named, chosen_name)
714         self.assertEqual(int(_int), names_input[chosen_name])
715
716     @given(integers(), integers(min_value=0), integers(min_value=0))
717     def test_bounds_satisfied(self, bound_min, bound_delta, value_delta):
718         value = bound_min + value_delta
719         bound_max = value + bound_delta
720         Integer(value=value, bounds=(bound_min, bound_max))
721
722     @given(sets(integers(), min_size=3, max_size=3))
723     def test_bounds_unsatisfied(self, values):
724         values = sorted(values)
725         with self.assertRaises(BoundsError) as err:
726             Integer(value=values[0], bounds=(values[1], values[2]))
727         repr(err.exception)
728         with self.assertRaises(BoundsError) as err:
729             Integer(value=values[2], bounds=(values[0], values[1]))
730         repr(err.exception)
731
732     @given(data_strategy())
733     def test_call(self, d):
734         for klass in (Integer, IntegerInherited):
735             (
736                 value_initial,
737                 bounds_initial,
738                 impl_initial,
739                 expl_initial,
740                 default_initial,
741                 optional_initial,
742                 _specs_initial,
743                 _decoded_initial,
744             ) = d.draw(integer_values_strategy())
745             obj_initial = klass(
746                 value_initial,
747                 bounds_initial,
748                 impl_initial,
749                 expl_initial,
750                 default_initial,
751                 optional_initial or False,
752                 _specs_initial,
753                 _decoded_initial,
754             )
755             (
756                 value,
757                 bounds,
758                 impl,
759                 expl,
760                 default,
761                 optional,
762                 _,
763                 _decoded,
764             ) = d.draw(integer_values_strategy(do_expl=impl_initial is None))
765             if (default is None) and (obj_initial.default is not None):
766                 bounds = None
767             if (
768                     (bounds is None) and
769                     (value is not None) and
770                     (bounds_initial is not None) and
771                     not (bounds_initial[0] <= value <= bounds_initial[1])
772             ):
773                 value = None
774             if (
775                     (bounds is None) and
776                     (default is not None) and
777                     (bounds_initial is not None) and
778                     not (bounds_initial[0] <= default <= bounds_initial[1])
779             ):
780                 default = None
781             obj = obj_initial(value, bounds, impl, expl, default, optional)
782             if obj.ready:
783                 value_expected = default if value is None else value
784                 value_expected = (
785                     default_initial if value_expected is None
786                     else value_expected
787                 )
788                 self.assertEqual(obj, value_expected)
789             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
790             self.assertEqual(obj.expl_tag, expl or expl_initial)
791             self.assertEqual(
792                 obj.default,
793                 default_initial if default is None else default,
794             )
795             if obj.default is None:
796                 optional = optional_initial if optional is None else optional
797                 optional = False if optional is None else optional
798             else:
799                 optional = True
800             self.assertEqual(obj.optional, optional)
801             self.assertEqual(
802                 (obj._bound_min, obj._bound_max),
803                 bounds or bounds_initial or (float("-inf"), float("+inf")),
804             )
805             self.assertEqual(
806                 obj.specs,
807                 {} if _specs_initial is None else dict(_specs_initial),
808             )
809
810     @given(integer_values_strategy())
811     def test_copy(self, values):
812         for klass in (Integer, IntegerInherited):
813             obj = klass(*values)
814             obj_copied = obj.copy()
815             self.assert_copied_basic_fields(obj, obj_copied)
816             self.assertEqual(obj.specs, obj_copied.specs)
817             self.assertEqual(obj._bound_min, obj_copied._bound_min)
818             self.assertEqual(obj._bound_max, obj_copied._bound_max)
819             self.assertEqual(obj._value, obj_copied._value)
820
821     @given(
822         integers(),
823         integers(min_value=1).map(tag_encode),
824     )
825     def test_stripped(self, value, tag_impl):
826         obj = Integer(value, impl=tag_impl)
827         with self.assertRaises(NotEnoughData):
828             obj.decode(obj.encode()[:-1])
829
830     @given(
831         integers(),
832         integers(min_value=1).map(tag_ctxc),
833     )
834     def test_stripped_expl(self, value, tag_expl):
835         obj = Integer(value, expl=tag_expl)
836         with self.assertRaises(NotEnoughData):
837             obj.decode(obj.encode()[:-1])
838
839     def test_zero_len(self):
840         with self.assertRaises(NotEnoughData):
841             Integer().decode(b"".join((
842                 Integer.tag_default,
843                 len_encode(0),
844             )))
845
846     @given(
847         integers(min_value=31),
848         integers(min_value=0),
849         lists(integers()),
850     )
851     def test_bad_tag(self, tag, offset, decode_path):
852         decode_path = tuple(str(i) for i in decode_path)
853         with self.assertRaises(DecodeError) as err:
854             Integer().decode(
855                 tag_encode(tag)[:-1],
856                 offset=offset,
857                 decode_path=decode_path,
858             )
859         repr(err.exception)
860         self.assertEqual(err.exception.offset, offset)
861         self.assertEqual(err.exception.decode_path, decode_path)
862
863     @given(
864         integers(min_value=128),
865         integers(min_value=0),
866         lists(integers()),
867     )
868     def test_bad_len(self, l, offset, decode_path):
869         decode_path = tuple(str(i) for i in decode_path)
870         with self.assertRaises(DecodeError) as err:
871             Integer().decode(
872                 Integer.tag_default + len_encode(l)[:-1],
873                 offset=offset,
874                 decode_path=decode_path,
875             )
876         repr(err.exception)
877         self.assertEqual(err.exception.offset, offset)
878         self.assertEqual(err.exception.decode_path, decode_path)
879
880     @given(
881         sets(integers(), min_size=2, max_size=2),
882         integers(min_value=0),
883         lists(integers()),
884     )
885     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
886         decode_path = tuple(str(i) for i in decode_path)
887         value, bound_min = list(sorted(ints))
888
889         class Int(Integer):
890             bounds = (bound_min, bound_min)
891         with self.assertRaises(DecodeError) as err:
892             Int().decode(
893                 Integer(value).encode(),
894                 offset=offset,
895                 decode_path=decode_path,
896             )
897         repr(err.exception)
898         self.assertEqual(err.exception.offset, offset)
899         self.assertEqual(err.exception.decode_path, decode_path)
900
901     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
902     @given(
903         integer_values_strategy(),
904         integers(),
905         integers(min_value=1).map(tag_ctxc),
906         integers(min_value=0),
907     )
908     def test_symmetric(self, values, value, tag_expl, offset):
909         for klass in (Integer, IntegerInherited):
910             _, _, _, _, default, optional, _, _decoded = values
911             obj = klass(
912                 value=value,
913                 default=default,
914                 optional=optional,
915                 _decoded=_decoded,
916             )
917             repr(obj)
918             pprint(obj)
919             self.assertFalse(obj.expled)
920             obj_encoded = obj.encode()
921             obj_expled = obj(value, expl=tag_expl)
922             self.assertTrue(obj_expled.expled)
923             repr(obj_expled)
924             pprint(obj_expled)
925             obj_expled_encoded = obj_expled.encode()
926             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
927             repr(obj_decoded)
928             pprint(obj_decoded)
929             self.assertEqual(tail, b"")
930             self.assertEqual(obj_decoded, obj_expled)
931             self.assertNotEqual(obj_decoded, obj)
932             self.assertEqual(int(obj_decoded), int(obj_expled))
933             self.assertEqual(int(obj_decoded), int(obj))
934             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
935             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
936             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
937             self.assertEqual(
938                 obj_decoded.expl_llen,
939                 len(len_encode(len(obj_encoded))),
940             )
941             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
942             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
943             self.assertEqual(
944                 obj_decoded.offset,
945                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
946             )
947             self.assertEqual(obj_decoded.expl_offset, offset)
948
949     def test_go_vectors_valid(self):
950         for data, expect in ((
951                 (b"\x00", 0),
952                 (b"\x7f", 127),
953                 (b"\x80", -128),
954                 (b"\xff\x7f", -129),
955                 (b"\xff", -1),
956                 (b"\x01", 1),
957                 (b"\x00\xff", 255),
958                 (b"\xff\x00", -256),
959                 (b"\x01\x00", 256),
960                 (b"\x00\x80", 128),
961                 (b"\x01\x00", 256),
962                 (b"\x80\x00\x00\x00\x00\x00\x00\x00", -9223372036854775808),
963                 (b"\x80\x00\x00\x00", -2147483648),
964         )):
965             self.assertEqual(
966                 Integer().decode(b"".join((
967                     Integer.tag_default,
968                     len_encode(len(data)),
969                     data,
970                 )))[0],
971                 expect,
972             )
973
974     def test_go_vectors_invalid(self):
975         for data in ((
976                 b"\x00\x7f",
977                 b"\xff\xf0",
978         )):
979             with self.assertRaises(DecodeError):
980                 Integer().decode(b"".join((
981                     Integer.tag_default,
982                     len_encode(len(data)),
983                     data,
984                 )))
985
986
987 @composite
988 def bit_string_values_strategy(draw, schema=None, value_required=False, do_expl=False):
989     if schema is None:
990         schema = ()
991         if draw(booleans()):
992             schema = draw(sets(text_letters(), min_size=1, max_size=256))
993             bits = draw(sets(
994                 integers(min_value=0, max_value=255),
995                 min_size=len(schema),
996                 max_size=len(schema),
997             ))
998             schema = list(zip(schema, bits))
999
1000     def _value(value_required):
1001         if not value_required and draw(booleans()):
1002             return
1003         generation_choice = 0
1004         if value_required:
1005             generation_choice = draw(sampled_from((1, 2, 3)))
1006         if generation_choice == 1 or draw(booleans()):
1007             return "'%s'B" % "".join(draw(lists(
1008                 sampled_from(("0", "1")),
1009                 max_size=len(schema),
1010             )))
1011         elif generation_choice == 2 or draw(booleans()):
1012             return draw(binary(max_size=len(schema) // 8))
1013         elif generation_choice == 3 or draw(booleans()):
1014             return tuple(draw(lists(sampled_from([name for name, _ in schema]))))
1015         return None
1016     value = _value(value_required)
1017     default = _value(value_required=False)
1018     impl = None
1019     expl = None
1020     if do_expl:
1021         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1022     else:
1023         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1024     optional = draw(one_of(none(), booleans()))
1025     _decoded = (
1026         draw(integers(min_value=0)),
1027         draw(integers(min_value=0)),
1028         draw(integers(min_value=0)),
1029     )
1030     return (schema, value, impl, expl, default, optional, _decoded)
1031
1032
1033 class BitStringInherited(BitString):
1034     pass
1035
1036
1037 class TestBitString(CommonMixin, TestCase):
1038     base_klass = BitString
1039
1040     @given(lists(booleans()))
1041     def test_b_encoding(self, bits):
1042         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1043         self.assertEqual(obj.bit_len, len(bits))
1044         self.assertSequenceEqual(list(obj), bits)
1045         for i, bit in enumerate(bits):
1046             self.assertEqual(obj[i], bit)
1047
1048     @given(lists(booleans()))
1049     def test_out_of_bounds_bits(self, bits):
1050         obj = BitString("'%s'B" % "".join("1" if bit else "0" for bit in bits))
1051         for i in range(len(bits), len(bits) * 2):
1052             self.assertFalse(obj[i])
1053
1054     def test_bad_b_encoding(self):
1055         with self.assertRaises(ValueError):
1056             BitString("'010120101'B")
1057
1058     @given(
1059         integers(min_value=1, max_value=255),
1060         integers(min_value=1, max_value=255),
1061     )
1062     def test_named_are_stripped(self, leading_zeros, trailing_zeros):
1063         obj = BitString("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1064         self.assertEqual(obj.bit_len, leading_zeros + 1 + trailing_zeros)
1065         self.assertGreater(len(obj.encode()), (leading_zeros + 1 + trailing_zeros) // 8)
1066
1067         class BS(BitString):
1068             schema = (("whatever", 0),)
1069         obj = BS("'%s1%s'B" % (("0" * leading_zeros), ("0" * trailing_zeros)))
1070         self.assertEqual(obj.bit_len, leading_zeros + 1)
1071         self.assertGreater(len(obj.encode()), (leading_zeros + 1) // 8)
1072
1073     def test_zero_len(self):
1074         with self.assertRaises(NotEnoughData):
1075             BitString().decode(b"".join((
1076                 BitString.tag_default,
1077                 len_encode(0),
1078             )))
1079
1080     def test_invalid_value_type(self):
1081         with self.assertRaises(InvalidValueType) as err:
1082             BitString(123)
1083         repr(err.exception)
1084         with self.assertRaises(InvalidValueType) as err:
1085             BitString(u"123")
1086         repr(err.exception)
1087
1088     def test_obj_unknown(self):
1089         with self.assertRaises(ObjUnknown) as err:
1090             BitString(b"whatever")["whenever"]
1091         repr(err.exception)
1092
1093     def test_get_invalid_type(self):
1094         with self.assertRaises(InvalidValueType) as err:
1095             BitString(b"whatever")[(1, 2, 3)]
1096         repr(err.exception)
1097
1098     @given(data_strategy())
1099     def test_unknown_name(self, d):
1100         _schema = d.draw(sets(text_letters(), min_size=2, max_size=5))
1101         missing = _schema.pop()
1102
1103         class BS(BitString):
1104             schema = [(n, i) for i, n in enumerate(_schema)]
1105         with self.assertRaises(ObjUnknown) as err:
1106             BS((missing,))
1107         repr(err.exception)
1108
1109     @given(booleans())
1110     def test_optional(self, optional):
1111         obj = BitString(default=BitString(b""), optional=optional)
1112         self.assertTrue(obj.optional)
1113
1114     @given(binary())
1115     def test_ready(self, value):
1116         obj = BitString()
1117         self.assertFalse(obj.ready)
1118         repr(obj)
1119         pprint(obj)
1120         with self.assertRaises(ObjNotReady) as err:
1121             obj.encode()
1122         repr(err.exception)
1123         obj = BitString(value)
1124         self.assertTrue(obj.ready)
1125         repr(obj)
1126         pprint(obj)
1127
1128     @given(
1129         tuples(integers(min_value=0), binary()),
1130         tuples(integers(min_value=0), binary()),
1131         binary(),
1132         binary(),
1133     )
1134     def test_comparison(self, value1, value2, tag1, tag2):
1135         for klass in (BitString, BitStringInherited):
1136             obj1 = klass(value1)
1137             obj2 = klass(value2)
1138             self.assertEqual(obj1 == obj2, value1 == value2)
1139             self.assertEqual(obj1 != obj2, value1 != value2)
1140             self.assertEqual(obj1 == bytes(obj2), value1[1] == value2[1])
1141             obj1 = klass(value1, impl=tag1)
1142             obj2 = klass(value1, impl=tag2)
1143             self.assertEqual(obj1 == obj2, tag1 == tag2)
1144             self.assertEqual(obj1 != obj2, tag1 != tag2)
1145
1146     @given(data_strategy())
1147     def test_call(self, d):
1148         for klass in (BitString, BitStringInherited):
1149             (
1150                 schema_initial,
1151                 value_initial,
1152                 impl_initial,
1153                 expl_initial,
1154                 default_initial,
1155                 optional_initial,
1156                 _decoded_initial,
1157             ) = d.draw(bit_string_values_strategy())
1158
1159             class BS(klass):
1160                 schema = schema_initial
1161             obj_initial = BS(
1162                 value=value_initial,
1163                 impl=impl_initial,
1164                 expl=expl_initial,
1165                 default=default_initial,
1166                 optional=optional_initial or False,
1167                 _decoded=_decoded_initial,
1168             )
1169             (
1170                 _,
1171                 value,
1172                 impl,
1173                 expl,
1174                 default,
1175                 optional,
1176                 _decoded,
1177             ) = d.draw(bit_string_values_strategy(
1178                 schema=schema_initial,
1179                 do_expl=impl_initial is None,
1180             ))
1181             obj = obj_initial(
1182                 value=value,
1183                 impl=impl,
1184                 expl=expl,
1185                 default=default,
1186                 optional=optional,
1187             )
1188             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1189             self.assertEqual(obj.expl_tag, expl or expl_initial)
1190             if obj.default is None:
1191                 optional = optional_initial if optional is None else optional
1192                 optional = False if optional is None else optional
1193             else:
1194                 optional = True
1195             self.assertEqual(obj.optional, optional)
1196             self.assertEqual(obj.specs, obj_initial.specs)
1197
1198     @given(bit_string_values_strategy())
1199     def test_copy(self, values):
1200         for klass in (BitString, BitStringInherited):
1201             _schema, value, impl, expl, default, optional, _decoded = values
1202
1203             class BS(klass):
1204                 schema = _schema
1205             obj = BS(
1206                 value=value,
1207                 impl=impl,
1208                 expl=expl,
1209                 default=default,
1210                 optional=optional or False,
1211                 _decoded=_decoded,
1212             )
1213             obj_copied = obj.copy()
1214             self.assert_copied_basic_fields(obj, obj_copied)
1215             self.assertEqual(obj.specs, obj_copied.specs)
1216             self.assertEqual(obj._value, obj_copied._value)
1217
1218     @given(
1219         binary(),
1220         integers(min_value=1).map(tag_encode),
1221     )
1222     def test_stripped(self, value, tag_impl):
1223         obj = BitString(value, impl=tag_impl)
1224         with self.assertRaises(NotEnoughData):
1225             obj.decode(obj.encode()[:-1])
1226
1227     @given(
1228         binary(),
1229         integers(min_value=1).map(tag_ctxc),
1230     )
1231     def test_stripped_expl(self, value, tag_expl):
1232         obj = BitString(value, expl=tag_expl)
1233         with self.assertRaises(NotEnoughData):
1234             obj.decode(obj.encode()[:-1])
1235
1236     @given(
1237         integers(min_value=31),
1238         integers(min_value=0),
1239         lists(integers()),
1240     )
1241     def test_bad_tag(self, tag, offset, decode_path):
1242         decode_path = tuple(str(i) for i in decode_path)
1243         with self.assertRaises(DecodeError) as err:
1244             BitString().decode(
1245                 tag_encode(tag)[:-1],
1246                 offset=offset,
1247                 decode_path=decode_path,
1248             )
1249         repr(err.exception)
1250         self.assertEqual(err.exception.offset, offset)
1251         self.assertEqual(err.exception.decode_path, decode_path)
1252
1253     @given(
1254         integers(min_value=128),
1255         integers(min_value=0),
1256         lists(integers()),
1257     )
1258     def test_bad_len(self, l, offset, decode_path):
1259         decode_path = tuple(str(i) for i in decode_path)
1260         with self.assertRaises(DecodeError) as err:
1261             BitString().decode(
1262                 BitString.tag_default + len_encode(l)[:-1],
1263                 offset=offset,
1264                 decode_path=decode_path,
1265             )
1266         repr(err.exception)
1267         self.assertEqual(err.exception.offset, offset)
1268         self.assertEqual(err.exception.decode_path, decode_path)
1269
1270     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1271     @given(data_strategy())
1272     def test_symmetric(self, d):
1273         (
1274             _schema,
1275             value,
1276             _,
1277             _,
1278             default,
1279             optional,
1280             _decoded,
1281         ) = d.draw(bit_string_values_strategy(value_required=True))
1282         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
1283         offset = d.draw(integers(min_value=0))
1284         for klass in (BitString, BitStringInherited):
1285             class BS(klass):
1286                 schema = _schema
1287             obj = BS(
1288                 value=value,
1289                 default=default,
1290                 optional=optional,
1291                 _decoded=_decoded,
1292             )
1293             repr(obj)
1294             pprint(obj)
1295             self.assertFalse(obj.expled)
1296             obj_encoded = obj.encode()
1297             obj_expled = obj(value, expl=tag_expl)
1298             self.assertTrue(obj_expled.expled)
1299             repr(obj_expled)
1300             pprint(obj_expled)
1301             obj_expled_encoded = obj_expled.encode()
1302             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
1303             repr(obj_decoded)
1304             pprint(obj_decoded)
1305             self.assertEqual(tail, b"")
1306             self.assertEqual(obj_decoded, obj_expled)
1307             self.assertNotEqual(obj_decoded, obj)
1308             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
1309             self.assertEqual(bytes(obj_decoded), bytes(obj))
1310             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1311             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1312             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1313             self.assertEqual(
1314                 obj_decoded.expl_llen,
1315                 len(len_encode(len(obj_encoded))),
1316             )
1317             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1318             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1319             self.assertEqual(
1320                 obj_decoded.offset,
1321                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1322             )
1323             self.assertEqual(obj_decoded.expl_offset, offset)
1324             if isinstance(value, tuple):
1325                 self.assertSetEqual(set(value), set(obj_decoded.named))
1326                 for name in value:
1327                     obj_decoded[name]
1328
1329     @given(integers(min_value=1, max_value=255))
1330     def test_bad_zero_value(self, pad_size):
1331         with self.assertRaises(DecodeError):
1332             BitString().decode(b"".join((
1333                 BitString.tag_default,
1334                 len_encode(1),
1335                 int2byte(pad_size),
1336             )))
1337
1338     def test_go_vectors_invalid(self):
1339         for data in ((
1340                 b"\x07\x01",
1341                 b"\x07\x40",
1342                 b"\x08\x00",
1343         )):
1344             with self.assertRaises(DecodeError):
1345                 BitString().decode(b"".join((
1346                     BitString.tag_default,
1347                     len_encode(2),
1348                     data,
1349                 )))
1350
1351     def test_go_vectors_valid(self):
1352         obj, _ = BitString().decode(b"".join((
1353             BitString.tag_default,
1354             len_encode(1),
1355             b"\x00",
1356         )))
1357         self.assertEqual(bytes(obj), b"")
1358         self.assertEqual(obj.bit_len, 0)
1359
1360         obj, _ = BitString().decode(b"".join((
1361             BitString.tag_default,
1362             len_encode(2),
1363             b"\x07\x00",
1364         )))
1365         self.assertEqual(bytes(obj), b"\x00")
1366         self.assertEqual(obj.bit_len, 1)
1367
1368         obj = BitString((16, b"\x82\x40"))
1369         self.assertTrue(obj[0])
1370         self.assertFalse(obj[1])
1371         self.assertTrue(obj[6])
1372         self.assertTrue(obj[9])
1373         self.assertFalse(obj[17])
1374
1375
1376 @composite
1377 def octet_string_values_strategy(draw, do_expl=False):
1378     bound_min, bound_max = sorted(draw(sets(
1379         integers(min_value=0, max_value=1 << 7),
1380         min_size=2,
1381         max_size=2,
1382     )))
1383     value = draw(one_of(
1384         none(),
1385         binary(min_size=bound_min, max_size=bound_max),
1386     ))
1387     default = draw(one_of(
1388         none(),
1389         binary(min_size=bound_min, max_size=bound_max),
1390     ))
1391     bounds = None
1392     if draw(booleans()):
1393         bounds = (bound_min, bound_max)
1394     impl = None
1395     expl = None
1396     if do_expl:
1397         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1398     else:
1399         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1400     optional = draw(one_of(none(), booleans()))
1401     _decoded = (
1402         draw(integers(min_value=0)),
1403         draw(integers(min_value=0)),
1404         draw(integers(min_value=0)),
1405     )
1406     return (value, bounds, impl, expl, default, optional, _decoded)
1407
1408
1409 class OctetStringInherited(OctetString):
1410     pass
1411
1412
1413 class TestOctetString(CommonMixin, TestCase):
1414     base_klass = OctetString
1415
1416     def test_invalid_value_type(self):
1417         with self.assertRaises(InvalidValueType) as err:
1418             OctetString(text_type(123))
1419         repr(err.exception)
1420
1421     @given(booleans())
1422     def test_optional(self, optional):
1423         obj = OctetString(default=OctetString(b""), optional=optional)
1424         self.assertTrue(obj.optional)
1425
1426     @given(binary())
1427     def test_ready(self, value):
1428         obj = OctetString()
1429         self.assertFalse(obj.ready)
1430         repr(obj)
1431         pprint(obj)
1432         with self.assertRaises(ObjNotReady) as err:
1433             obj.encode()
1434         repr(err.exception)
1435         obj = OctetString(value)
1436         self.assertTrue(obj.ready)
1437         repr(obj)
1438         pprint(obj)
1439
1440     @given(binary(), binary(), binary(), binary())
1441     def test_comparison(self, value1, value2, tag1, tag2):
1442         for klass in (OctetString, OctetStringInherited):
1443             obj1 = klass(value1)
1444             obj2 = klass(value2)
1445             self.assertEqual(obj1 == obj2, value1 == value2)
1446             self.assertEqual(obj1 != obj2, value1 != value2)
1447             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
1448             obj1 = klass(value1, impl=tag1)
1449             obj2 = klass(value1, impl=tag2)
1450             self.assertEqual(obj1 == obj2, tag1 == tag2)
1451             self.assertEqual(obj1 != obj2, tag1 != tag2)
1452
1453     @given(lists(binary()))
1454     def test_sorted_works(self, values):
1455         self.assertSequenceEqual(
1456             [bytes(v) for v in sorted(OctetString(v) for v in values)],
1457             sorted(values),
1458         )
1459
1460     @given(data_strategy())
1461     def test_bounds_satisfied(self, d):
1462         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
1463         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1464         value = d.draw(binary(min_size=bound_min, max_size=bound_max))
1465         OctetString(value=value, bounds=(bound_min, bound_max))
1466
1467     @given(data_strategy())
1468     def test_bounds_unsatisfied(self, d):
1469         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
1470         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
1471         value = d.draw(binary(max_size=bound_min - 1))
1472         with self.assertRaises(BoundsError) as err:
1473             OctetString(value=value, bounds=(bound_min, bound_max))
1474         repr(err.exception)
1475         value = d.draw(binary(min_size=bound_max + 1))
1476         with self.assertRaises(BoundsError) as err:
1477             OctetString(value=value, bounds=(bound_min, bound_max))
1478         repr(err.exception)
1479
1480     @given(data_strategy())
1481     def test_call(self, d):
1482         for klass in (OctetString, OctetStringInherited):
1483             (
1484                 value_initial,
1485                 bounds_initial,
1486                 impl_initial,
1487                 expl_initial,
1488                 default_initial,
1489                 optional_initial,
1490                 _decoded_initial,
1491             ) = d.draw(octet_string_values_strategy())
1492             obj_initial = klass(
1493                 value_initial,
1494                 bounds_initial,
1495                 impl_initial,
1496                 expl_initial,
1497                 default_initial,
1498                 optional_initial or False,
1499                 _decoded_initial,
1500             )
1501             (
1502                 value,
1503                 bounds,
1504                 impl,
1505                 expl,
1506                 default,
1507                 optional,
1508                 _decoded,
1509             ) = d.draw(octet_string_values_strategy(do_expl=impl_initial is None))
1510             if (default is None) and (obj_initial.default is not None):
1511                 bounds = None
1512             if (
1513                     (bounds is None) and
1514                     (value is not None) and
1515                     (bounds_initial is not None) and
1516                     not (bounds_initial[0] <= len(value) <= bounds_initial[1])
1517             ):
1518                 value = None
1519             if (
1520                     (bounds is None) and
1521                     (default is not None) and
1522                     (bounds_initial is not None) and
1523                     not (bounds_initial[0] <= len(default) <= bounds_initial[1])
1524             ):
1525                 default = None
1526             obj = obj_initial(value, bounds, impl, expl, default, optional)
1527             if obj.ready:
1528                 value_expected = default if value is None else value
1529                 value_expected = (
1530                     default_initial if value_expected is None
1531                     else value_expected
1532                 )
1533                 self.assertEqual(obj, value_expected)
1534             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1535             self.assertEqual(obj.expl_tag, expl or expl_initial)
1536             self.assertEqual(
1537                 obj.default,
1538                 default_initial if default is None else default,
1539             )
1540             if obj.default is None:
1541                 optional = optional_initial if optional is None else optional
1542                 optional = False if optional is None else optional
1543             else:
1544                 optional = True
1545             self.assertEqual(obj.optional, optional)
1546             self.assertEqual(
1547                 (obj._bound_min, obj._bound_max),
1548                 bounds or bounds_initial or (0, float("+inf")),
1549             )
1550
1551     @given(octet_string_values_strategy())
1552     def test_copy(self, values):
1553         for klass in (OctetString, OctetStringInherited):
1554             obj = klass(*values)
1555             obj_copied = obj.copy()
1556             self.assert_copied_basic_fields(obj, obj_copied)
1557             self.assertEqual(obj._bound_min, obj_copied._bound_min)
1558             self.assertEqual(obj._bound_max, obj_copied._bound_max)
1559             self.assertEqual(obj._value, obj_copied._value)
1560
1561     @given(
1562         binary(),
1563         integers(min_value=1).map(tag_encode),
1564     )
1565     def test_stripped(self, value, tag_impl):
1566         obj = OctetString(value, impl=tag_impl)
1567         with self.assertRaises(NotEnoughData):
1568             obj.decode(obj.encode()[:-1])
1569
1570     @given(
1571         binary(),
1572         integers(min_value=1).map(tag_ctxc),
1573     )
1574     def test_stripped_expl(self, value, tag_expl):
1575         obj = OctetString(value, expl=tag_expl)
1576         with self.assertRaises(NotEnoughData):
1577             obj.decode(obj.encode()[:-1])
1578
1579     @given(
1580         integers(min_value=31),
1581         integers(min_value=0),
1582         lists(integers()),
1583     )
1584     def test_bad_tag(self, tag, offset, decode_path):
1585         decode_path = tuple(str(i) for i in decode_path)
1586         with self.assertRaises(DecodeError) as err:
1587             OctetString().decode(
1588                 tag_encode(tag)[:-1],
1589                 offset=offset,
1590                 decode_path=decode_path,
1591             )
1592         repr(err.exception)
1593         self.assertEqual(err.exception.offset, offset)
1594         self.assertEqual(err.exception.decode_path, decode_path)
1595
1596     @given(
1597         integers(min_value=128),
1598         integers(min_value=0),
1599         lists(integers()),
1600     )
1601     def test_bad_len(self, l, offset, decode_path):
1602         decode_path = tuple(str(i) for i in decode_path)
1603         with self.assertRaises(DecodeError) as err:
1604             OctetString().decode(
1605                 OctetString.tag_default + len_encode(l)[:-1],
1606                 offset=offset,
1607                 decode_path=decode_path,
1608             )
1609         repr(err.exception)
1610         self.assertEqual(err.exception.offset, offset)
1611         self.assertEqual(err.exception.decode_path, decode_path)
1612
1613     @given(
1614         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
1615         integers(min_value=0),
1616         lists(integers()),
1617     )
1618     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
1619         decode_path = tuple(str(i) for i in decode_path)
1620         value, bound_min = list(sorted(ints))
1621
1622         class String(OctetString):
1623             bounds = (bound_min, bound_min)
1624         with self.assertRaises(DecodeError) as err:
1625             String().decode(
1626                 OctetString(b"\x00" * value).encode(),
1627                 offset=offset,
1628                 decode_path=decode_path,
1629             )
1630         repr(err.exception)
1631         self.assertEqual(err.exception.offset, offset)
1632         self.assertEqual(err.exception.decode_path, decode_path)
1633
1634     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
1635     @given(
1636         octet_string_values_strategy(),
1637         binary(),
1638         integers(min_value=1).map(tag_ctxc),
1639         integers(min_value=0),
1640     )
1641     def test_symmetric(self, values, value, tag_expl, offset):
1642         for klass in (OctetString, OctetStringInherited):
1643             _, _, _, _, default, optional, _decoded = values
1644             obj = klass(
1645                 value=value,
1646                 default=default,
1647                 optional=optional,
1648                 _decoded=_decoded,
1649             )
1650             repr(obj)
1651             pprint(obj)
1652             self.assertFalse(obj.expled)
1653             obj_encoded = obj.encode()
1654             obj_expled = obj(value, expl=tag_expl)
1655             self.assertTrue(obj_expled.expled)
1656             repr(obj_expled)
1657             pprint(obj_expled)
1658             obj_expled_encoded = obj_expled.encode()
1659             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
1660             repr(obj_decoded)
1661             pprint(obj_decoded)
1662             self.assertEqual(tail, b"")
1663             self.assertEqual(obj_decoded, obj_expled)
1664             self.assertNotEqual(obj_decoded, obj)
1665             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
1666             self.assertEqual(bytes(obj_decoded), bytes(obj))
1667             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1668             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1669             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1670             self.assertEqual(
1671                 obj_decoded.expl_llen,
1672                 len(len_encode(len(obj_encoded))),
1673             )
1674             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1675             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1676             self.assertEqual(
1677                 obj_decoded.offset,
1678                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1679             )
1680             self.assertEqual(obj_decoded.expl_offset, offset)
1681
1682
1683 @composite
1684 def null_values_strategy(draw, do_expl=False):
1685     impl = None
1686     expl = None
1687     if do_expl:
1688         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1689     else:
1690         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1691     optional = draw(one_of(none(), booleans()))
1692     _decoded = (
1693         draw(integers(min_value=0)),
1694         draw(integers(min_value=0)),
1695         draw(integers(min_value=0)),
1696     )
1697     return (impl, expl, optional, _decoded)
1698
1699
1700 class NullInherited(Null):
1701     pass
1702
1703
1704 class TestNull(CommonMixin, TestCase):
1705     base_klass = Null
1706
1707     def test_ready(self):
1708         obj = Null()
1709         self.assertTrue(obj.ready)
1710         repr(obj)
1711         pprint(obj)
1712
1713     @given(binary(), binary())
1714     def test_comparison(self, tag1, tag2):
1715         for klass in (Null, NullInherited):
1716             obj1 = klass(impl=tag1)
1717             obj2 = klass(impl=tag2)
1718             self.assertEqual(obj1 == obj2, tag1 == tag2)
1719             self.assertEqual(obj1 != obj2, tag1 != tag2)
1720             self.assertNotEqual(obj1, tag2)
1721
1722     @given(data_strategy())
1723     def test_call(self, d):
1724         for klass in (Null, NullInherited):
1725             (
1726                 impl_initial,
1727                 expl_initial,
1728                 optional_initial,
1729                 _decoded_initial,
1730             ) = d.draw(null_values_strategy())
1731             obj_initial = klass(
1732                 impl=impl_initial,
1733                 expl=expl_initial,
1734                 optional=optional_initial or False,
1735                 _decoded=_decoded_initial,
1736             )
1737             (
1738                 impl,
1739                 expl,
1740                 optional,
1741                 _decoded,
1742             ) = d.draw(null_values_strategy(do_expl=impl_initial is None))
1743             obj = obj_initial(impl=impl, expl=expl, optional=optional)
1744             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1745             self.assertEqual(obj.expl_tag, expl or expl_initial)
1746             optional = optional_initial if optional is None else optional
1747             optional = False if optional is None else optional
1748             self.assertEqual(obj.optional, optional)
1749
1750     @given(null_values_strategy())
1751     def test_copy(self, values):
1752         for klass in (Null, NullInherited):
1753             impl, expl, optional, _decoded = values
1754             obj = klass(
1755                 impl=impl,
1756                 expl=expl,
1757                 optional=optional or False,
1758                 _decoded=_decoded,
1759             )
1760             obj_copied = obj.copy()
1761             self.assert_copied_basic_fields(obj, obj_copied)
1762
1763     @given(integers(min_value=1).map(tag_encode))
1764     def test_stripped(self, tag_impl):
1765         obj = Null(impl=tag_impl)
1766         with self.assertRaises(NotEnoughData):
1767             obj.decode(obj.encode()[:-1])
1768
1769     @given(integers(min_value=1).map(tag_ctxc))
1770     def test_stripped_expl(self, tag_expl):
1771         obj = Null(expl=tag_expl)
1772         with self.assertRaises(NotEnoughData):
1773             obj.decode(obj.encode()[:-1])
1774
1775     @given(
1776         integers(min_value=31),
1777         integers(min_value=0),
1778         lists(integers()),
1779     )
1780     def test_bad_tag(self, tag, offset, decode_path):
1781         decode_path = tuple(str(i) for i in decode_path)
1782         with self.assertRaises(DecodeError) as err:
1783             Null().decode(
1784                 tag_encode(tag)[:-1],
1785                 offset=offset,
1786                 decode_path=decode_path,
1787             )
1788         repr(err.exception)
1789         self.assertEqual(err.exception.offset, offset)
1790         self.assertEqual(err.exception.decode_path, decode_path)
1791
1792     @given(
1793         integers(min_value=128),
1794         integers(min_value=0),
1795         lists(integers()),
1796     )
1797     def test_bad_len(self, l, offset, decode_path):
1798         decode_path = tuple(str(i) for i in decode_path)
1799         with self.assertRaises(DecodeError) as err:
1800             Null().decode(
1801                 Null.tag_default + len_encode(l)[:-1],
1802                 offset=offset,
1803                 decode_path=decode_path,
1804             )
1805         repr(err.exception)
1806         self.assertEqual(err.exception.offset, offset)
1807         self.assertEqual(err.exception.decode_path, decode_path)
1808
1809     @given(binary(min_size=1))
1810     def test_tag_mismatch(self, impl):
1811         assume(impl != Null.tag_default)
1812         with self.assertRaises(TagMismatch):
1813             Null(impl=impl).decode(Null().encode())
1814
1815     @given(
1816         null_values_strategy(),
1817         integers(min_value=1).map(tag_ctxc),
1818         integers(min_value=0),
1819     )
1820     def test_symmetric(self, values, tag_expl, offset):
1821         for klass in (Null, NullInherited):
1822             _, _, optional, _decoded = values
1823             obj = klass(optional=optional, _decoded=_decoded)
1824             repr(obj)
1825             pprint(obj)
1826             self.assertFalse(obj.expled)
1827             obj_encoded = obj.encode()
1828             obj_expled = obj(expl=tag_expl)
1829             self.assertTrue(obj_expled.expled)
1830             repr(obj_expled)
1831             pprint(obj_expled)
1832             obj_expled_encoded = obj_expled.encode()
1833             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
1834             repr(obj_decoded)
1835             pprint(obj_decoded)
1836             self.assertEqual(tail, b"")
1837             self.assertEqual(obj_decoded, obj_expled)
1838             self.assertNotEqual(obj_decoded, obj)
1839             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
1840             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
1841             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
1842             self.assertEqual(
1843                 obj_decoded.expl_llen,
1844                 len(len_encode(len(obj_encoded))),
1845             )
1846             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
1847             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
1848             self.assertEqual(
1849                 obj_decoded.offset,
1850                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
1851             )
1852             self.assertEqual(obj_decoded.expl_offset, offset)
1853
1854     @given(integers(min_value=1))
1855     def test_invalid_len(self, l):
1856         with self.assertRaises(InvalidLength):
1857             Null().decode(b"".join((
1858                 Null.tag_default,
1859                 len_encode(l),
1860             )))
1861
1862
1863 @composite
1864 def oid_strategy(draw):
1865     first_arc = draw(integers(min_value=0, max_value=2))
1866     second_arc = 0
1867     if first_arc in (0, 1):
1868         second_arc = draw(integers(min_value=0, max_value=39))
1869     else:
1870         second_arc = draw(integers(min_value=0))
1871     other_arcs = draw(lists(integers(min_value=0)))
1872     return tuple([first_arc, second_arc] + other_arcs)
1873
1874
1875 @composite
1876 def oid_values_strategy(draw, do_expl=False):
1877     value = draw(one_of(none(), oid_strategy()))
1878     impl = None
1879     expl = None
1880     if do_expl:
1881         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1882     else:
1883         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
1884     default = draw(one_of(none(), oid_strategy()))
1885     optional = draw(one_of(none(), booleans()))
1886     _decoded = (
1887         draw(integers(min_value=0)),
1888         draw(integers(min_value=0)),
1889         draw(integers(min_value=0)),
1890     )
1891     return (value, impl, expl, default, optional, _decoded)
1892
1893
1894 class ObjectIdentifierInherited(ObjectIdentifier):
1895     pass
1896
1897
1898 class TestObjectIdentifier(CommonMixin, TestCase):
1899     base_klass = ObjectIdentifier
1900
1901     def test_invalid_value_type(self):
1902         with self.assertRaises(InvalidValueType) as err:
1903             ObjectIdentifier(123)
1904         repr(err.exception)
1905
1906     @given(booleans())
1907     def test_optional(self, optional):
1908         obj = ObjectIdentifier(default=ObjectIdentifier("1.2.3"), optional=optional)
1909         self.assertTrue(obj.optional)
1910
1911     @given(oid_strategy())
1912     def test_ready(self, value):
1913         obj = ObjectIdentifier()
1914         self.assertFalse(obj.ready)
1915         repr(obj)
1916         pprint(obj)
1917         with self.assertRaises(ObjNotReady) as err:
1918             obj.encode()
1919         repr(err.exception)
1920         obj = ObjectIdentifier(value)
1921         self.assertTrue(obj.ready)
1922         repr(obj)
1923         pprint(obj)
1924         hash(obj)
1925
1926     @given(oid_strategy(), oid_strategy(), binary(), binary())
1927     def test_comparison(self, value1, value2, tag1, tag2):
1928         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
1929             obj1 = klass(value1)
1930             obj2 = klass(value2)
1931             self.assertEqual(obj1 == obj2, value1 == value2)
1932             self.assertEqual(obj1 != obj2, value1 != value2)
1933             self.assertEqual(obj1 == tuple(obj2), value1 == value2)
1934             self.assertEqual(str(obj1) == str(obj2), value1 == value2)
1935             obj1 = klass(value1, impl=tag1)
1936             obj2 = klass(value1, impl=tag2)
1937             self.assertEqual(obj1 == obj2, tag1 == tag2)
1938             self.assertEqual(obj1 != obj2, tag1 != tag2)
1939
1940     @given(lists(oid_strategy()))
1941     def test_sorted_works(self, values):
1942         self.assertSequenceEqual(
1943             [tuple(v) for v in sorted(ObjectIdentifier(v) for v in values)],
1944             sorted(values),
1945         )
1946
1947     @given(data_strategy())
1948     def test_call(self, d):
1949         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
1950             (
1951                 value_initial,
1952                 impl_initial,
1953                 expl_initial,
1954                 default_initial,
1955                 optional_initial,
1956                 _decoded_initial,
1957             ) = d.draw(oid_values_strategy())
1958             obj_initial = klass(
1959                 value=value_initial,
1960                 impl=impl_initial,
1961                 expl=expl_initial,
1962                 default=default_initial,
1963                 optional=optional_initial or False,
1964                 _decoded=_decoded_initial,
1965             )
1966             (
1967                 value,
1968                 impl,
1969                 expl,
1970                 default,
1971                 optional,
1972                 _decoded,
1973             ) = d.draw(oid_values_strategy(do_expl=impl_initial is None))
1974             obj = obj_initial(
1975                 value=value,
1976                 impl=impl,
1977                 expl=expl,
1978                 default=default,
1979                 optional=optional,
1980             )
1981             if obj.ready:
1982                 value_expected = default if value is None else value
1983                 value_expected = (
1984                     default_initial if value_expected is None
1985                     else value_expected
1986                 )
1987                 self.assertEqual(obj, value_expected)
1988             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
1989             self.assertEqual(obj.expl_tag, expl or expl_initial)
1990             self.assertEqual(
1991                 obj.default,
1992                 default_initial if default is None else default,
1993             )
1994             if obj.default is None:
1995                 optional = optional_initial if optional is None else optional
1996                 optional = False if optional is None else optional
1997             else:
1998                 optional = True
1999             self.assertEqual(obj.optional, optional)
2000
2001     @given(oid_values_strategy())
2002     def test_copy(self, values):
2003         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2004             (
2005                 value,
2006                 impl,
2007                 expl,
2008                 default,
2009                 optional,
2010                 _decoded,
2011             ) = values
2012             obj = klass(
2013                 value=value,
2014                 impl=impl,
2015                 expl=expl,
2016                 default=default,
2017                 optional=optional,
2018                 _decoded=_decoded,
2019             )
2020             obj_copied = obj.copy()
2021             self.assert_copied_basic_fields(obj, obj_copied)
2022             self.assertEqual(obj._value, obj_copied._value)
2023
2024     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2025     @given(
2026         oid_strategy(),
2027         integers(min_value=1).map(tag_encode),
2028     )
2029     def test_stripped(self, value, tag_impl):
2030         obj = ObjectIdentifier(value, impl=tag_impl)
2031         with self.assertRaises(NotEnoughData):
2032             obj.decode(obj.encode()[:-1])
2033
2034     @given(
2035         oid_strategy(),
2036         integers(min_value=1).map(tag_ctxc),
2037     )
2038     def test_stripped_expl(self, value, tag_expl):
2039         obj = ObjectIdentifier(value, expl=tag_expl)
2040         with self.assertRaises(NotEnoughData):
2041             obj.decode(obj.encode()[:-1])
2042
2043     @given(
2044         integers(min_value=31),
2045         integers(min_value=0),
2046         lists(integers()),
2047     )
2048     def test_bad_tag(self, tag, offset, decode_path):
2049         decode_path = tuple(str(i) for i in decode_path)
2050         with self.assertRaises(DecodeError) as err:
2051             ObjectIdentifier().decode(
2052                 tag_encode(tag)[:-1],
2053                 offset=offset,
2054                 decode_path=decode_path,
2055             )
2056         repr(err.exception)
2057         self.assertEqual(err.exception.offset, offset)
2058         self.assertEqual(err.exception.decode_path, decode_path)
2059
2060     @given(
2061         integers(min_value=128),
2062         integers(min_value=0),
2063         lists(integers()),
2064     )
2065     def test_bad_len(self, l, offset, decode_path):
2066         decode_path = tuple(str(i) for i in decode_path)
2067         with self.assertRaises(DecodeError) as err:
2068             ObjectIdentifier().decode(
2069                 ObjectIdentifier.tag_default + len_encode(l)[:-1],
2070                 offset=offset,
2071                 decode_path=decode_path,
2072             )
2073         repr(err.exception)
2074         self.assertEqual(err.exception.offset, offset)
2075         self.assertEqual(err.exception.decode_path, decode_path)
2076
2077     def test_zero_oid(self):
2078         with self.assertRaises(NotEnoughData):
2079             ObjectIdentifier().decode(
2080                 b"".join((ObjectIdentifier.tag_default, len_encode(0)))
2081             )
2082
2083     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2084     @given(oid_strategy())
2085     def test_unfinished_oid(self, value):
2086         assume(list(value)[-1] > 255)
2087         obj_encoded = ObjectIdentifier(value).encode()
2088         obj, _ = ObjectIdentifier().decode(obj_encoded)
2089         data = obj_encoded[obj.tlen + obj.llen:-1]
2090         data = b"".join((
2091             ObjectIdentifier.tag_default,
2092             len_encode(len(data)),
2093             data,
2094         ))
2095         with assertRaisesRegex(self, DecodeError, "unfinished OID"):
2096             obj.decode(data)
2097
2098     @given(integers(min_value=0))
2099     def test_invalid_short(self, value):
2100         with self.assertRaises(InvalidOID):
2101             ObjectIdentifier((value,))
2102         with self.assertRaises(InvalidOID):
2103             ObjectIdentifier("%d" % value)
2104
2105     @given(integers(min_value=3), integers(min_value=0))
2106     def test_invalid_first_arc(self, first_arc, second_arc):
2107         with self.assertRaises(InvalidOID):
2108             ObjectIdentifier((first_arc, second_arc))
2109         with self.assertRaises(InvalidOID):
2110             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2111
2112     @given(integers(min_value=0, max_value=1), integers(min_value=40))
2113     def test_invalid_second_arc(self, first_arc, second_arc):
2114         with self.assertRaises(InvalidOID):
2115             ObjectIdentifier((first_arc, second_arc))
2116         with self.assertRaises(InvalidOID):
2117             ObjectIdentifier("%d.%d" % (first_arc, second_arc))
2118
2119     @given(text(alphabet=ascii_letters + ".", min_size=1))
2120     def test_junk(self, oid):
2121         with self.assertRaises(InvalidOID):
2122             ObjectIdentifier(oid)
2123
2124     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2125     @given(oid_strategy())
2126     def test_validness(self, oid):
2127         obj = ObjectIdentifier(oid)
2128         self.assertEqual(obj, ObjectIdentifier(".".join(str(arc) for arc in oid)))
2129         str(obj)
2130         repr(obj)
2131         pprint(obj)
2132
2133     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2134     @given(
2135         oid_values_strategy(),
2136         oid_strategy(),
2137         integers(min_value=1).map(tag_ctxc),
2138         integers(min_value=0),
2139     )
2140     def test_symmetric(self, values, value, tag_expl, offset):
2141         for klass in (ObjectIdentifier, ObjectIdentifierInherited):
2142             _, _, _, default, optional, _decoded = values
2143             obj = klass(
2144                 value=value,
2145                 default=default,
2146                 optional=optional,
2147                 _decoded=_decoded,
2148             )
2149             repr(obj)
2150             pprint(obj)
2151             self.assertFalse(obj.expled)
2152             obj_encoded = obj.encode()
2153             obj_expled = obj(value, expl=tag_expl)
2154             self.assertTrue(obj_expled.expled)
2155             repr(obj_expled)
2156             pprint(obj_expled)
2157             obj_expled_encoded = obj_expled.encode()
2158             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
2159             repr(obj_decoded)
2160             pprint(obj_decoded)
2161             self.assertEqual(tail, b"")
2162             self.assertEqual(obj_decoded, obj_expled)
2163             self.assertNotEqual(obj_decoded, obj)
2164             self.assertEqual(tuple(obj_decoded), tuple(obj_expled))
2165             self.assertEqual(tuple(obj_decoded), tuple(obj))
2166             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2167             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2168             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2169             self.assertEqual(
2170                 obj_decoded.expl_llen,
2171                 len(len_encode(len(obj_encoded))),
2172             )
2173             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2174             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2175             self.assertEqual(
2176                 obj_decoded.offset,
2177                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2178             )
2179             self.assertEqual(obj_decoded.expl_offset, offset)
2180
2181     @given(
2182         oid_strategy().map(ObjectIdentifier),
2183         oid_strategy().map(ObjectIdentifier),
2184     )
2185     def test_add(self, oid1, oid2):
2186         oid_expect = ObjectIdentifier(str(oid1) + "." + str(oid2))
2187         for oid_to_add in (oid2, tuple(oid2)):
2188             self.assertEqual(oid1 + oid_to_add, oid_expect)
2189         with self.assertRaises(InvalidValueType):
2190             oid1 + str(oid2)
2191
2192     def test_go_vectors_valid(self):
2193         for data, expect in (
2194                 (b"\x55", (2, 5)),
2195                 (b"\x55\x02", (2, 5, 2)),
2196                 (b"\x55\x02\xc0\x00", (2, 5, 2, 8192)),
2197                 (b"\x81\x34\x03", (2, 100, 3)),
2198         ):
2199             self.assertEqual(
2200                 ObjectIdentifier().decode(b"".join((
2201                     ObjectIdentifier.tag_default,
2202                     len_encode(len(data)),
2203                     data,
2204                 )))[0],
2205                 expect,
2206             )
2207
2208     def test_go_vectors_invalid(self):
2209         data = b"\x55\x02\xc0\x80\x80\x80\x80"
2210         with self.assertRaises(DecodeError):
2211             ObjectIdentifier().decode(b"".join((
2212                 Integer.tag_default,
2213                 len_encode(len(data)),
2214                 data,
2215             )))
2216
2217
2218 @composite
2219 def enumerated_values_strategy(draw, schema=None, do_expl=False):
2220     if schema is None:
2221         schema = list(draw(sets(text_printable, min_size=1, max_size=3)))
2222         values = list(draw(sets(
2223             integers(),
2224             min_size=len(schema),
2225             max_size=len(schema),
2226         )))
2227         schema = list(zip(schema, values))
2228     value = draw(one_of(none(), sampled_from([k for k, v in schema])))
2229     impl = None
2230     expl = None
2231     if do_expl:
2232         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2233     else:
2234         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2235     default = draw(one_of(none(), sampled_from([v for k, v in schema])))
2236     optional = draw(one_of(none(), booleans()))
2237     _decoded = (
2238         draw(integers(min_value=0)),
2239         draw(integers(min_value=0)),
2240         draw(integers(min_value=0)),
2241     )
2242     return (schema, value, impl, expl, default, optional, _decoded)
2243
2244
2245 class TestEnumerated(CommonMixin, TestCase):
2246     class EWhatever(Enumerated):
2247         schema = (("whatever", 0),)
2248
2249     base_klass = EWhatever
2250
2251     def test_schema_required(self):
2252         with assertRaisesRegex(self, ValueError, "schema must be specified"):
2253             Enumerated()
2254
2255     def test_invalid_value_type(self):
2256         with self.assertRaises(InvalidValueType) as err:
2257             self.base_klass((1, 2))
2258         repr(err.exception)
2259
2260     @given(sets(text_letters(), min_size=2))
2261     def test_unknown_name(self, schema_input):
2262         missing = schema_input.pop()
2263
2264         class E(Enumerated):
2265             schema = [(n, 123) for n in schema_input]
2266         with self.assertRaises(ObjUnknown) as err:
2267             E(missing)
2268         repr(err.exception)
2269
2270     @given(
2271         sets(text_letters(), min_size=2),
2272         sets(integers(), min_size=2),
2273     )
2274     def test_unknown_value(self, schema_input, values_input):
2275         schema_input.pop()
2276         missing_value = values_input.pop()
2277         _input = list(zip(schema_input, values_input))
2278
2279         class E(Enumerated):
2280             schema = _input
2281         with self.assertRaises(DecodeError) as err:
2282             E(missing_value)
2283         repr(err.exception)
2284
2285     @given(booleans())
2286     def test_optional(self, optional):
2287         obj = self.base_klass(default="whatever", optional=optional)
2288         self.assertTrue(obj.optional)
2289
2290     def test_ready(self):
2291         obj = self.base_klass()
2292         self.assertFalse(obj.ready)
2293         repr(obj)
2294         pprint(obj)
2295         with self.assertRaises(ObjNotReady) as err:
2296             obj.encode()
2297         repr(err.exception)
2298         obj = self.base_klass("whatever")
2299         self.assertTrue(obj.ready)
2300         repr(obj)
2301         pprint(obj)
2302
2303     @given(integers(), integers(), binary(), binary())
2304     def test_comparison(self, value1, value2, tag1, tag2):
2305         class E(Enumerated):
2306             schema = (
2307                 ("whatever0", value1),
2308                 ("whatever1", value2),
2309             )
2310
2311         class EInherited(E):
2312             pass
2313         for klass in (E, EInherited):
2314             obj1 = klass(value1)
2315             obj2 = klass(value2)
2316             self.assertEqual(obj1 == obj2, value1 == value2)
2317             self.assertEqual(obj1 != obj2, value1 != value2)
2318             self.assertEqual(obj1 == int(obj2), value1 == value2)
2319             obj1 = klass(value1, impl=tag1)
2320             obj2 = klass(value1, impl=tag2)
2321             self.assertEqual(obj1 == obj2, tag1 == tag2)
2322             self.assertEqual(obj1 != obj2, tag1 != tag2)
2323
2324     @given(data_strategy())
2325     def test_call(self, d):
2326         (
2327             schema_initial,
2328             value_initial,
2329             impl_initial,
2330             expl_initial,
2331             default_initial,
2332             optional_initial,
2333             _decoded_initial,
2334         ) = d.draw(enumerated_values_strategy())
2335
2336         class E(Enumerated):
2337             schema = schema_initial
2338         obj_initial = E(
2339             value=value_initial,
2340             impl=impl_initial,
2341             expl=expl_initial,
2342             default=default_initial,
2343             optional=optional_initial or False,
2344             _decoded=_decoded_initial,
2345         )
2346         (
2347             _,
2348             value,
2349             impl,
2350             expl,
2351             default,
2352             optional,
2353             _decoded,
2354         ) = d.draw(enumerated_values_strategy(
2355             schema=schema_initial,
2356             do_expl=impl_initial is None,
2357         ))
2358         obj = obj_initial(
2359             value=value,
2360             impl=impl,
2361             expl=expl,
2362             default=default,
2363             optional=optional,
2364         )
2365         if obj.ready:
2366             value_expected = default if value is None else value
2367             value_expected = (
2368                 default_initial if value_expected is None
2369                 else value_expected
2370             )
2371             self.assertEqual(
2372                 int(obj),
2373                 dict(schema_initial).get(value_expected, value_expected),
2374             )
2375         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2376         self.assertEqual(obj.expl_tag, expl or expl_initial)
2377         self.assertEqual(
2378             obj.default,
2379             default_initial if default is None else default,
2380         )
2381         if obj.default is None:
2382             optional = optional_initial if optional is None else optional
2383             optional = False if optional is None else optional
2384         else:
2385             optional = True
2386         self.assertEqual(obj.optional, optional)
2387         self.assertEqual(obj.specs, dict(schema_initial))
2388
2389     @given(enumerated_values_strategy())
2390     def test_copy(self, values):
2391         schema_input, value, impl, expl, default, optional, _decoded = values
2392
2393         class E(Enumerated):
2394             schema = schema_input
2395         obj = E(
2396             value=value,
2397             impl=impl,
2398             expl=expl,
2399             default=default,
2400             optional=optional,
2401             _decoded=_decoded,
2402         )
2403         obj_copied = obj.copy()
2404         self.assert_copied_basic_fields(obj, obj_copied)
2405         self.assertEqual(obj.specs, obj_copied.specs)
2406
2407     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2408     @given(data_strategy())
2409     def test_symmetric(self, d):
2410         schema_input, _, _, _, default, optional, _decoded = d.draw(
2411             enumerated_values_strategy(),
2412         )
2413         tag_expl = d.draw(integers(min_value=1).map(tag_ctxc))
2414         offset = d.draw(integers(min_value=0))
2415         value = d.draw(sampled_from(sorted([v for _, v in schema_input])))
2416
2417         class E(Enumerated):
2418             schema = schema_input
2419         obj = E(
2420             value=value,
2421             default=default,
2422             optional=optional,
2423             _decoded=_decoded,
2424         )
2425         repr(obj)
2426         pprint(obj)
2427         self.assertFalse(obj.expled)
2428         obj_encoded = obj.encode()
2429         obj_expled = obj(value, expl=tag_expl)
2430         self.assertTrue(obj_expled.expled)
2431         repr(obj_expled)
2432         pprint(obj_expled)
2433         obj_expled_encoded = obj_expled.encode()
2434         obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
2435         repr(obj_decoded)
2436         pprint(obj_decoded)
2437         self.assertEqual(tail, b"")
2438         self.assertEqual(obj_decoded, obj_expled)
2439         self.assertNotEqual(obj_decoded, obj)
2440         self.assertEqual(int(obj_decoded), int(obj_expled))
2441         self.assertEqual(int(obj_decoded), int(obj))
2442         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2443         self.assertEqual(obj_decoded.expl_tag, tag_expl)
2444         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2445         self.assertEqual(
2446             obj_decoded.expl_llen,
2447             len(len_encode(len(obj_encoded))),
2448         )
2449         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2450         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2451         self.assertEqual(
2452             obj_decoded.offset,
2453             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2454         )
2455         self.assertEqual(obj_decoded.expl_offset, offset)
2456
2457
2458 @composite
2459 def string_values_strategy(draw, alphabet, do_expl=False):
2460     bound_min, bound_max = sorted(draw(sets(
2461         integers(min_value=0, max_value=1 << 7),
2462         min_size=2,
2463         max_size=2,
2464     )))
2465     value = draw(one_of(
2466         none(),
2467         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
2468     ))
2469     default = draw(one_of(
2470         none(),
2471         text(alphabet=alphabet, min_size=bound_min, max_size=bound_max),
2472     ))
2473     bounds = None
2474     if draw(booleans()):
2475         bounds = (bound_min, bound_max)
2476     impl = None
2477     expl = None
2478     if do_expl:
2479         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2480     else:
2481         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2482     optional = draw(one_of(none(), booleans()))
2483     _decoded = (
2484         draw(integers(min_value=0)),
2485         draw(integers(min_value=0)),
2486         draw(integers(min_value=0)),
2487     )
2488     return (value, bounds, impl, expl, default, optional, _decoded)
2489
2490
2491 class StringMixin(object):
2492     def test_invalid_value_type(self):
2493         with self.assertRaises(InvalidValueType) as err:
2494             self.base_klass((1, 2))
2495         repr(err.exception)
2496
2497     def text_alphabet(self):
2498         if self.base_klass.encoding in ("ascii", "iso-8859-1"):
2499             return printable + whitespace
2500         return None
2501
2502     @given(booleans())
2503     def test_optional(self, optional):
2504         obj = self.base_klass(default=self.base_klass(""), optional=optional)
2505         self.assertTrue(obj.optional)
2506
2507     @given(data_strategy())
2508     def test_ready(self, d):
2509         obj = self.base_klass()
2510         self.assertFalse(obj.ready)
2511         repr(obj)
2512         pprint(obj)
2513         text_type(obj)
2514         with self.assertRaises(ObjNotReady) as err:
2515             obj.encode()
2516         repr(err.exception)
2517         value = d.draw(text(alphabet=self.text_alphabet()))
2518         obj = self.base_klass(value)
2519         self.assertTrue(obj.ready)
2520         repr(obj)
2521         pprint(obj)
2522         text_type(obj)
2523
2524     @given(data_strategy())
2525     def test_comparison(self, d):
2526         value1 = d.draw(text(alphabet=self.text_alphabet()))
2527         value2 = d.draw(text(alphabet=self.text_alphabet()))
2528         tag1 = d.draw(binary())
2529         tag2 = d.draw(binary())
2530         obj1 = self.base_klass(value1)
2531         obj2 = self.base_klass(value2)
2532         self.assertEqual(obj1 == obj2, value1 == value2)
2533         self.assertEqual(obj1 != obj2, value1 != value2)
2534         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
2535         self.assertEqual(obj1 == text_type(obj2), value1 == value2)
2536         obj1 = self.base_klass(value1, impl=tag1)
2537         obj2 = self.base_klass(value1, impl=tag2)
2538         self.assertEqual(obj1 == obj2, tag1 == tag2)
2539         self.assertEqual(obj1 != obj2, tag1 != tag2)
2540
2541     @given(data_strategy())
2542     def test_bounds_satisfied(self, d):
2543         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
2544         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
2545         value = d.draw(text(
2546             alphabet=self.text_alphabet(),
2547             min_size=bound_min,
2548             max_size=bound_max,
2549         ))
2550         self.base_klass(value=value, bounds=(bound_min, bound_max))
2551
2552     @given(data_strategy())
2553     def test_bounds_unsatisfied(self, d):
2554         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
2555         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
2556         value = d.draw(text(alphabet=self.text_alphabet(), max_size=bound_min - 1))
2557         with self.assertRaises(BoundsError) as err:
2558             self.base_klass(value=value, bounds=(bound_min, bound_max))
2559         repr(err.exception)
2560         value = d.draw(text(alphabet=self.text_alphabet(), min_size=bound_max + 1))
2561         with self.assertRaises(BoundsError) as err:
2562             self.base_klass(value=value, bounds=(bound_min, bound_max))
2563         repr(err.exception)
2564
2565     @given(data_strategy())
2566     def test_call(self, d):
2567         (
2568             value_initial,
2569             bounds_initial,
2570             impl_initial,
2571             expl_initial,
2572             default_initial,
2573             optional_initial,
2574             _decoded_initial,
2575         ) = d.draw(string_values_strategy(self.text_alphabet()))
2576         obj_initial = self.base_klass(
2577             value_initial,
2578             bounds_initial,
2579             impl_initial,
2580             expl_initial,
2581             default_initial,
2582             optional_initial or False,
2583             _decoded_initial,
2584         )
2585         (
2586             value,
2587             bounds,
2588             impl,
2589             expl,
2590             default,
2591             optional,
2592             _decoded,
2593         ) = d.draw(string_values_strategy(
2594             self.text_alphabet(),
2595             do_expl=impl_initial is None,
2596         ))
2597         if (default is None) and (obj_initial.default is not None):
2598             bounds = None
2599         if (
2600                 (bounds is None) and
2601                 (value is not None) and
2602                 (bounds_initial is not None) and
2603                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
2604         ):
2605             value = None
2606         if (
2607                 (bounds is None) and
2608                 (default is not None) and
2609                 (bounds_initial is not None) and
2610                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
2611         ):
2612             default = None
2613         obj = obj_initial(value, bounds, impl, expl, default, optional)
2614         if obj.ready:
2615             value_expected = default if value is None else value
2616             value_expected = (
2617                 default_initial if value_expected is None
2618                 else value_expected
2619             )
2620             self.assertEqual(obj, value_expected)
2621         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2622         self.assertEqual(obj.expl_tag, expl or expl_initial)
2623         self.assertEqual(
2624             obj.default,
2625             default_initial if default is None else default,
2626         )
2627         if obj.default is None:
2628             optional = optional_initial if optional is None else optional
2629             optional = False if optional is None else optional
2630         else:
2631             optional = True
2632         self.assertEqual(obj.optional, optional)
2633         self.assertEqual(
2634             (obj._bound_min, obj._bound_max),
2635             bounds or bounds_initial or (0, float("+inf")),
2636         )
2637
2638     @given(data_strategy())
2639     def test_copy(self, d):
2640         values = d.draw(string_values_strategy(self.text_alphabet()))
2641         obj = self.base_klass(*values)
2642         obj_copied = obj.copy()
2643         self.assert_copied_basic_fields(obj, obj_copied)
2644         self.assertEqual(obj._bound_min, obj_copied._bound_min)
2645         self.assertEqual(obj._bound_max, obj_copied._bound_max)
2646         self.assertEqual(obj._value, obj_copied._value)
2647
2648     @given(data_strategy())
2649     def test_stripped(self, d):
2650         value = d.draw(text(alphabet=self.text_alphabet()))
2651         tag_impl = tag_encode(d.draw(integers(min_value=1)))
2652         obj = self.base_klass(value, impl=tag_impl)
2653         with self.assertRaises(NotEnoughData):
2654             obj.decode(obj.encode()[:-1])
2655
2656     @given(data_strategy())
2657     def test_stripped_expl(self, d):
2658         value = d.draw(text(alphabet=self.text_alphabet()))
2659         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
2660         obj = self.base_klass(value, expl=tag_expl)
2661         with self.assertRaises(NotEnoughData):
2662             obj.decode(obj.encode()[:-1])
2663
2664     @given(
2665         integers(min_value=31),
2666         integers(min_value=0),
2667         lists(integers()),
2668     )
2669     def test_bad_tag(self, tag, offset, decode_path):
2670         decode_path = tuple(str(i) for i in decode_path)
2671         with self.assertRaises(DecodeError) as err:
2672             self.base_klass().decode(
2673                 tag_encode(tag)[:-1],
2674                 offset=offset,
2675                 decode_path=decode_path,
2676             )
2677         repr(err.exception)
2678         self.assertEqual(err.exception.offset, offset)
2679         self.assertEqual(err.exception.decode_path, decode_path)
2680
2681     @given(
2682         integers(min_value=128),
2683         integers(min_value=0),
2684         lists(integers()),
2685     )
2686     def test_bad_len(self, l, offset, decode_path):
2687         decode_path = tuple(str(i) for i in decode_path)
2688         with self.assertRaises(DecodeError) as err:
2689             self.base_klass().decode(
2690                 self.base_klass.tag_default + len_encode(l)[:-1],
2691                 offset=offset,
2692                 decode_path=decode_path,
2693             )
2694         repr(err.exception)
2695         self.assertEqual(err.exception.offset, offset)
2696         self.assertEqual(err.exception.decode_path, decode_path)
2697
2698     @given(
2699         sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
2700         integers(min_value=0),
2701         lists(integers()),
2702     )
2703     def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
2704         decode_path = tuple(str(i) for i in decode_path)
2705         value, bound_min = list(sorted(ints))
2706
2707         class String(self.base_klass):
2708             # Multiply this value by four, to satisfy UTF-32 bounds
2709             # (4 bytes per character) validation
2710             bounds = (bound_min * 4, bound_min * 4)
2711         with self.assertRaises(DecodeError) as err:
2712             String().decode(
2713                 self.base_klass(b"\x00\x00\x00\x00" * value).encode(),
2714                 offset=offset,
2715                 decode_path=decode_path,
2716             )
2717         repr(err.exception)
2718         self.assertEqual(err.exception.offset, offset)
2719         self.assertEqual(err.exception.decode_path, decode_path)
2720
2721     @given(data_strategy())
2722     def test_symmetric(self, d):
2723         values = d.draw(string_values_strategy(self.text_alphabet()))
2724         value = d.draw(text(alphabet=self.text_alphabet()))
2725         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
2726         offset = d.draw(integers(min_value=0))
2727         _, _, _, _, default, optional, _decoded = values
2728         obj = self.base_klass(
2729             value=value,
2730             default=default,
2731             optional=optional,
2732             _decoded=_decoded,
2733         )
2734         repr(obj)
2735         pprint(obj)
2736         self.assertFalse(obj.expled)
2737         obj_encoded = obj.encode()
2738         obj_expled = obj(value, expl=tag_expl)
2739         self.assertTrue(obj_expled.expled)
2740         repr(obj_expled)
2741         pprint(obj_expled)
2742         obj_expled_encoded = obj_expled.encode()
2743         obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
2744         repr(obj_decoded)
2745         pprint(obj_decoded)
2746         self.assertEqual(tail, b"")
2747         self.assertEqual(obj_decoded, obj_expled)
2748         self.assertNotEqual(obj_decoded, obj)
2749         self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
2750         self.assertEqual(bytes(obj_decoded), bytes(obj))
2751         self.assertEqual(text_type(obj_decoded), text_type(obj_expled))
2752         self.assertEqual(text_type(obj_decoded), text_type(obj))
2753         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
2754         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
2755         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
2756         self.assertEqual(
2757             obj_decoded.expl_llen,
2758             len(len_encode(len(obj_encoded))),
2759         )
2760         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
2761         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
2762         self.assertEqual(
2763             obj_decoded.offset,
2764             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
2765         )
2766         self.assertEqual(obj_decoded.expl_offset, offset)
2767
2768
2769 class TestUTF8String(StringMixin, CommonMixin, TestCase):
2770     base_klass = UTF8String
2771
2772
2773 class TestNumericString(StringMixin, CommonMixin, TestCase):
2774     base_klass = NumericString
2775
2776
2777 class TestPrintableString(StringMixin, CommonMixin, TestCase):
2778     base_klass = PrintableString
2779
2780
2781 class TestTeletexString(StringMixin, CommonMixin, TestCase):
2782     base_klass = TeletexString
2783
2784
2785 class TestVideotexString(StringMixin, CommonMixin, TestCase):
2786     base_klass = VideotexString
2787
2788
2789 class TestIA5String(StringMixin, CommonMixin, TestCase):
2790     base_klass = IA5String
2791
2792
2793 class TestGraphicString(StringMixin, CommonMixin, TestCase):
2794     base_klass = GraphicString
2795
2796
2797 class TestVisibleString(StringMixin, CommonMixin, TestCase):
2798     base_klass = VisibleString
2799
2800
2801 class TestGeneralString(StringMixin, CommonMixin, TestCase):
2802     base_klass = GeneralString
2803
2804
2805 class TestUniversalString(StringMixin, CommonMixin, TestCase):
2806     base_klass = UniversalString
2807
2808
2809 class TestBMPString(StringMixin, CommonMixin, TestCase):
2810     base_klass = BMPString
2811
2812
2813 @composite
2814 def generalized_time_values_strategy(
2815         draw,
2816         min_datetime,
2817         max_datetime,
2818         omit_ms=False,
2819         do_expl=False,
2820 ):
2821     value = None
2822     if draw(booleans()):
2823         value = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
2824         if omit_ms:
2825             value = value.replace(microsecond=0)
2826     default = None
2827     if draw(booleans()):
2828         default = draw(datetimes(min_value=min_datetime, max_value=max_datetime))
2829         if omit_ms:
2830             default = default.replace(microsecond=0)
2831     impl = None
2832     expl = None
2833     if do_expl:
2834         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2835     else:
2836         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
2837     optional = draw(one_of(none(), booleans()))
2838     _decoded = (
2839         draw(integers(min_value=0)),
2840         draw(integers(min_value=0)),
2841         draw(integers(min_value=0)),
2842     )
2843     return (value, impl, expl, default, optional, _decoded)
2844
2845
2846 class TimeMixin(object):
2847     def test_invalid_value_type(self):
2848         with self.assertRaises(InvalidValueType) as err:
2849             self.base_klass(datetime.now().timetuple())
2850         repr(err.exception)
2851
2852     @given(data_strategy())
2853     def test_optional(self, d):
2854         default = d.draw(datetimes(
2855             min_value=self.min_datetime,
2856             max_value=self.max_datetime,
2857         ))
2858         optional = d.draw(booleans())
2859         obj = self.base_klass(default=default, optional=optional)
2860         self.assertTrue(obj.optional)
2861
2862     @given(data_strategy())
2863     def test_ready(self, d):
2864         obj = self.base_klass()
2865         self.assertFalse(obj.ready)
2866         repr(obj)
2867         pprint(obj)
2868         with self.assertRaises(ObjNotReady) as err:
2869             obj.encode()
2870         repr(err.exception)
2871         value = d.draw(datetimes(min_value=self.min_datetime))
2872         obj = self.base_klass(value)
2873         self.assertTrue(obj.ready)
2874         repr(obj)
2875         pprint(obj)
2876
2877     @given(data_strategy())
2878     def test_comparison(self, d):
2879         value1 = d.draw(datetimes(
2880             min_value=self.min_datetime,
2881             max_value=self.max_datetime,
2882         ))
2883         value2 = d.draw(datetimes(
2884             min_value=self.min_datetime,
2885             max_value=self.max_datetime,
2886         ))
2887         tag1 = d.draw(binary())
2888         tag2 = d.draw(binary())
2889         if self.omit_ms:
2890             value1 = value1.replace(microsecond=0)
2891             value2 = value2.replace(microsecond=0)
2892         obj1 = self.base_klass(value1)
2893         obj2 = self.base_klass(value2)
2894         self.assertEqual(obj1 == obj2, value1 == value2)
2895         self.assertEqual(obj1 != obj2, value1 != value2)
2896         self.assertEqual(obj1 == obj2.todatetime(), value1 == value2)
2897         self.assertEqual(obj1 == bytes(obj2), value1 == value2)
2898         obj1 = self.base_klass(value1, impl=tag1)
2899         obj2 = self.base_klass(value1, impl=tag2)
2900         self.assertEqual(obj1 == obj2, tag1 == tag2)
2901         self.assertEqual(obj1 != obj2, tag1 != tag2)
2902
2903     @given(data_strategy())
2904     def test_call(self, d):
2905         (
2906             value_initial,
2907             impl_initial,
2908             expl_initial,
2909             default_initial,
2910             optional_initial,
2911             _decoded_initial,
2912         ) = d.draw(generalized_time_values_strategy(
2913             min_datetime=self.min_datetime,
2914             max_datetime=self.max_datetime,
2915             omit_ms=self.omit_ms,
2916         ))
2917         obj_initial = self.base_klass(
2918             value=value_initial,
2919             impl=impl_initial,
2920             expl=expl_initial,
2921             default=default_initial,
2922             optional=optional_initial or False,
2923             _decoded=_decoded_initial,
2924         )
2925         (
2926             value,
2927             impl,
2928             expl,
2929             default,
2930             optional,
2931             _decoded,
2932         ) = d.draw(generalized_time_values_strategy(
2933             min_datetime=self.min_datetime,
2934             max_datetime=self.max_datetime,
2935             omit_ms=self.omit_ms,
2936             do_expl=impl_initial is None,
2937         ))
2938         obj = obj_initial(
2939             value=value,
2940             impl=impl,
2941             expl=expl,
2942             default=default,
2943             optional=optional,
2944         )
2945         if obj.ready:
2946             value_expected = default if value is None else value
2947             value_expected = (
2948                 default_initial if value_expected is None
2949                 else value_expected
2950             )
2951             self.assertEqual(obj, value_expected)
2952         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
2953         self.assertEqual(obj.expl_tag, expl or expl_initial)
2954         self.assertEqual(
2955             obj.default,
2956             default_initial if default is None else default,
2957         )
2958         if obj.default is None:
2959             optional = optional_initial if optional is None else optional
2960             optional = False if optional is None else optional
2961         else:
2962             optional = True
2963         self.assertEqual(obj.optional, optional)
2964
2965     @given(data_strategy())
2966     def test_copy(self, d):
2967         values = d.draw(generalized_time_values_strategy(
2968             min_datetime=self.min_datetime,
2969             max_datetime=self.max_datetime,
2970         ))
2971         obj = self.base_klass(*values)
2972         obj_copied = obj.copy()
2973         self.assert_copied_basic_fields(obj, obj_copied)
2974         self.assertEqual(obj._value, obj_copied._value)
2975
2976     @given(data_strategy())
2977     def test_stripped(self, d):
2978         value = d.draw(datetimes(
2979             min_value=self.min_datetime,
2980             max_value=self.max_datetime,
2981         ))
2982         tag_impl = tag_encode(d.draw(integers(min_value=1)))
2983         obj = self.base_klass(value, impl=tag_impl)
2984         with self.assertRaises(NotEnoughData):
2985             obj.decode(obj.encode()[:-1])
2986
2987     @given(data_strategy())
2988     def test_stripped_expl(self, d):
2989         value = d.draw(datetimes(
2990             min_value=self.min_datetime,
2991             max_value=self.max_datetime,
2992         ))
2993         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
2994         obj = self.base_klass(value, expl=tag_expl)
2995         with self.assertRaises(NotEnoughData):
2996             obj.decode(obj.encode()[:-1])
2997
2998     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
2999     @given(data_strategy())
3000     def test_symmetric(self, d):
3001         values = d.draw(generalized_time_values_strategy(
3002             min_datetime=self.min_datetime,
3003             max_datetime=self.max_datetime,
3004         ))
3005         value = d.draw(datetimes(
3006             min_value=self.min_datetime,
3007             max_value=self.max_datetime,
3008         ))
3009         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3010         offset = d.draw(integers(min_value=0))
3011         _, _, _, default, optional, _decoded = values
3012         obj = self.base_klass(
3013             value=value,
3014             default=default,
3015             optional=optional,
3016             _decoded=_decoded,
3017         )
3018         repr(obj)
3019         pprint(obj)
3020         self.assertFalse(obj.expled)
3021         obj_encoded = obj.encode()
3022         obj_expled = obj(value, expl=tag_expl)
3023         self.assertTrue(obj_expled.expled)
3024         repr(obj_expled)
3025         pprint(obj_expled)
3026         obj_expled_encoded = obj_expled.encode()
3027         obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
3028         repr(obj_decoded)
3029         pprint(obj_decoded)
3030         self.assertEqual(tail, b"")
3031         self.assertEqual(obj_decoded, obj_expled)
3032         self.assertEqual(obj_decoded.todatetime(), obj_expled.todatetime())
3033         self.assertEqual(obj_decoded.todatetime(), obj.todatetime())
3034         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3035         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3036         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3037         self.assertEqual(
3038             obj_decoded.expl_llen,
3039             len(len_encode(len(obj_encoded))),
3040         )
3041         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3042         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3043         self.assertEqual(
3044             obj_decoded.offset,
3045             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3046         )
3047         self.assertEqual(obj_decoded.expl_offset, offset)
3048
3049
3050 class TestGeneralizedTime(TimeMixin, CommonMixin, TestCase):
3051     base_klass = GeneralizedTime
3052     omit_ms = False
3053     min_datetime = datetime(1900, 1, 1)
3054     max_datetime = datetime(9999, 12, 31)
3055
3056     def test_go_vectors_invalid(self):
3057         for data in ((
3058                 b"20100102030405",
3059                 b"00000100000000Z",
3060                 b"20101302030405Z",
3061                 b"20100002030405Z",
3062                 b"20100100030405Z",
3063                 b"20100132030405Z",
3064                 b"20100231030405Z",
3065                 b"20100102240405Z",
3066                 b"20100102036005Z",
3067                 b"20100102030460Z",
3068                 b"-20100102030410Z",
3069                 b"2010-0102030410Z",
3070                 b"2010-0002030410Z",
3071                 b"201001-02030410Z",
3072                 b"20100102-030410Z",
3073                 b"2010010203-0410Z",
3074                 b"201001020304-10Z",
3075                 # These ones are INVALID in *DER*, but accepted
3076                 # by Go's encoding/asn1
3077                 b"20100102030405+0607",
3078                 b"20100102030405-0607",
3079         )):
3080             with self.assertRaises(DecodeError) as err:
3081                 GeneralizedTime(data)
3082             repr(err.exception)
3083
3084     def test_go_vectors_valid(self):
3085         self.assertEqual(
3086             GeneralizedTime(b"20100102030405Z").todatetime(),
3087             datetime(2010, 1, 2, 3, 4, 5, 0),
3088         )
3089
3090
3091 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
3092     base_klass = UTCTime
3093     omit_ms = True
3094     min_datetime = datetime(2000, 1, 1)
3095     max_datetime = datetime(2049, 12, 31)
3096
3097     def test_go_vectors_invalid(self):
3098         for data in ((
3099                 b"a10506234540Z",
3100                 b"91a506234540Z",
3101                 b"9105a6234540Z",
3102                 b"910506a34540Z",
3103                 b"910506334a40Z",
3104                 b"91050633444aZ",
3105                 b"910506334461Z",
3106                 b"910506334400Za",
3107                 b"000100000000Z",
3108                 b"101302030405Z",
3109                 b"100002030405Z",
3110                 b"100100030405Z",
3111                 b"100132030405Z",
3112                 b"100231030405Z",
3113                 b"100102240405Z",
3114                 b"100102036005Z",
3115                 b"100102030460Z",
3116                 b"-100102030410Z",
3117                 b"10-0102030410Z",
3118                 b"10-0002030410Z",
3119                 b"1001-02030410Z",
3120                 b"100102-030410Z",
3121                 b"10010203-0410Z",
3122                 b"1001020304-10Z",
3123                 # These ones are INVALID in *DER*, but accepted
3124                 # by Go's encoding/asn1
3125                 b"910506164540-0700",
3126                 b"910506164540+0730",
3127                 b"9105062345Z",
3128                 b"5105062345Z",
3129         )):
3130             with self.assertRaises(DecodeError) as err:
3131                 UTCTime(data)
3132             repr(err.exception)
3133
3134     def test_go_vectors_valid(self):
3135         self.assertEqual(
3136             UTCTime(b"910506234540Z").todatetime(),
3137             datetime(1991, 5, 6, 23, 45, 40, 0),
3138         )
3139
3140     @given(integers(min_value=0, max_value=49))
3141     def test_pre50(self, year):
3142         self.assertEqual(
3143             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3144             2000 + year,
3145         )
3146
3147     @given(integers(min_value=50, max_value=99))
3148     def test_post50(self, year):
3149         self.assertEqual(
3150             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3151             1900 + year,
3152         )
3153
3154
3155 @composite
3156 def any_values_strategy(draw, do_expl=False):
3157     value = draw(one_of(none(), binary()))
3158     expl = None
3159     if do_expl:
3160         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3161     optional = draw(one_of(none(), booleans()))
3162     _decoded = (
3163         draw(integers(min_value=0)),
3164         draw(integers(min_value=0)),
3165         draw(integers(min_value=0)),
3166     )
3167     return (value, expl, optional, _decoded)
3168
3169
3170 class AnyInherited(Any):
3171     pass
3172
3173
3174 class TestAny(CommonMixin, TestCase):
3175     base_klass = Any
3176
3177     def test_invalid_value_type(self):
3178         with self.assertRaises(InvalidValueType) as err:
3179             Any(123)
3180         repr(err.exception)
3181
3182     @given(booleans())
3183     def test_optional(self, optional):
3184         obj = Any(optional=optional)
3185         self.assertEqual(obj.optional, optional)
3186
3187     @given(binary())
3188     def test_ready(self, value):
3189         obj = Any()
3190         self.assertFalse(obj.ready)
3191         repr(obj)
3192         pprint(obj)
3193         with self.assertRaises(ObjNotReady) as err:
3194             obj.encode()
3195         repr(err.exception)
3196         obj = Any(value)
3197         self.assertTrue(obj.ready)
3198         repr(obj)
3199         pprint(obj)
3200
3201     @given(integers())
3202     def test_basic(self, value):
3203         integer_encoded = Integer(value).encode()
3204         for obj in (
3205                 Any(integer_encoded),
3206                 Any(Integer(value)),
3207                 Any(Any(Integer(value))),
3208         ):
3209             self.assertSequenceEqual(bytes(obj), integer_encoded)
3210             self.assertEqual(
3211                 obj.decode(obj.encode())[0].vlen,
3212                 len(integer_encoded),
3213             )
3214             repr(obj)
3215             pprint(obj)
3216             self.assertSequenceEqual(obj.encode(), integer_encoded)
3217
3218     @given(binary(), binary())
3219     def test_comparison(self, value1, value2):
3220         for klass in (Any, AnyInherited):
3221             obj1 = klass(value1)
3222             obj2 = klass(value2)
3223             self.assertEqual(obj1 == obj2, value1 == value2)
3224             self.assertEqual(obj1 != obj2, value1 != value2)
3225             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3226
3227     @given(data_strategy())
3228     def test_call(self, d):
3229         for klass in (Any, AnyInherited):
3230             (
3231                 value_initial,
3232                 expl_initial,
3233                 optional_initial,
3234                 _decoded_initial,
3235             ) = d.draw(any_values_strategy())
3236             obj_initial = klass(
3237                 value_initial,
3238                 expl_initial,
3239                 optional_initial or False,
3240                 _decoded_initial,
3241             )
3242             (
3243                 value,
3244                 expl,
3245                 optional,
3246                 _decoded,
3247             ) = d.draw(any_values_strategy(do_expl=True))
3248             obj = obj_initial(value, expl, optional)
3249             if obj.ready:
3250                 value_expected = None if value is None else value
3251                 self.assertEqual(obj, value_expected)
3252             self.assertEqual(obj.expl_tag, expl or expl_initial)
3253             if obj.default is None:
3254                 optional = optional_initial if optional is None else optional
3255                 optional = False if optional is None else optional
3256             self.assertEqual(obj.optional, optional)
3257
3258     def test_simultaneous_impl_expl(self):
3259         # override it, as Any does not have implicit tag
3260         pass
3261
3262     def test_decoded(self):
3263         # override it, as Any does not have implicit tag
3264         pass
3265
3266     @given(any_values_strategy())
3267     def test_copy(self, values):
3268         for klass in (Any, AnyInherited):
3269             obj = klass(*values)
3270             obj_copied = obj.copy()
3271             self.assert_copied_basic_fields(obj, obj_copied)
3272             self.assertEqual(obj._value, obj_copied._value)
3273
3274     @given(binary().map(OctetString))
3275     def test_stripped(self, value):
3276         obj = Any(value)
3277         with self.assertRaises(NotEnoughData):
3278             obj.decode(obj.encode()[:-1])
3279
3280     @given(
3281         binary(),
3282         integers(min_value=1).map(tag_ctxc),
3283     )
3284     def test_stripped_expl(self, value, tag_expl):
3285         obj = Any(value, expl=tag_expl)
3286         with self.assertRaises(NotEnoughData):
3287             obj.decode(obj.encode()[:-1])
3288
3289     @given(
3290         integers(min_value=31),
3291         integers(min_value=0),
3292         lists(integers()),
3293     )
3294     def test_bad_tag(self, tag, offset, decode_path):
3295         decode_path = tuple(str(i) for i in decode_path)
3296         with self.assertRaises(DecodeError) as err:
3297             Any().decode(
3298                 tag_encode(tag)[:-1],
3299                 offset=offset,
3300                 decode_path=decode_path,
3301             )
3302         repr(err.exception)
3303         self.assertEqual(err.exception.offset, offset)
3304         self.assertEqual(err.exception.decode_path, decode_path)
3305
3306     @given(
3307         integers(min_value=128),
3308         integers(min_value=0),
3309         lists(integers()),
3310     )
3311     def test_bad_len(self, l, offset, decode_path):
3312         decode_path = tuple(str(i) for i in decode_path)
3313         with self.assertRaises(DecodeError) as err:
3314             Any().decode(
3315                 Any.tag_default + len_encode(l)[:-1],
3316                 offset=offset,
3317                 decode_path=decode_path,
3318             )
3319         repr(err.exception)
3320         self.assertEqual(err.exception.offset, offset)
3321         self.assertEqual(err.exception.decode_path, decode_path)
3322
3323     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3324     @given(
3325         any_values_strategy(),
3326         integers().map(lambda x: Integer(x).encode()),
3327         integers(min_value=1).map(tag_ctxc),
3328         integers(min_value=0),
3329     )
3330     def test_symmetric(self, values, value, tag_expl, offset):
3331         for klass in (Any, AnyInherited):
3332             _, _, optional, _decoded = values
3333             obj = klass(value=value, optional=optional, _decoded=_decoded)
3334             repr(obj)
3335             pprint(obj)
3336             self.assertFalse(obj.expled)
3337             obj_encoded = obj.encode()
3338             obj_expled = obj(value, expl=tag_expl)
3339             self.assertTrue(obj_expled.expled)
3340             repr(obj_expled)
3341             pprint(obj_expled)
3342             obj_expled_encoded = obj_expled.encode()
3343             obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
3344             repr(obj_decoded)
3345             pprint(obj_decoded)
3346             self.assertEqual(tail, b"")
3347             self.assertEqual(obj_decoded, obj_expled)
3348             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3349             self.assertEqual(bytes(obj_decoded), bytes(obj))
3350             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3351             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3352             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3353             self.assertEqual(
3354                 obj_decoded.expl_llen,
3355                 len(len_encode(len(obj_encoded))),
3356             )
3357             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3358             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3359             self.assertEqual(
3360                 obj_decoded.offset,
3361                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3362             )
3363             self.assertEqual(obj_decoded.expl_offset, offset)
3364             self.assertEqual(obj_decoded.tlen, 0)
3365             self.assertEqual(obj_decoded.llen, 0)
3366             self.assertEqual(obj_decoded.vlen, len(value))
3367
3368
3369 @composite
3370 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
3371     if schema is None:
3372         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
3373         tags = [tag_encode(tag) for tag in draw(sets(
3374             integers(min_value=0),
3375             min_size=len(names),
3376             max_size=len(names),
3377         ))]
3378         schema = [(name, Integer(impl=tag)) for name, tag in zip(names, tags)]
3379     value = None
3380     if value_required or draw(booleans()):
3381         value = draw(tuples(
3382             sampled_from([name for name, _ in schema]),
3383             integers().map(Integer),
3384         ))
3385     expl = None
3386     if do_expl:
3387         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3388     default = draw(one_of(
3389         none(),
3390         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
3391     ))
3392     optional = draw(one_of(none(), booleans()))
3393     _decoded = (
3394         draw(integers(min_value=0)),
3395         draw(integers(min_value=0)),
3396         draw(integers(min_value=0)),
3397     )
3398     return (schema, value, expl, default, optional, _decoded)
3399
3400
3401 class ChoiceInherited(Choice):
3402     pass
3403
3404
3405 class TestChoice(CommonMixin, TestCase):
3406     class Wahl(Choice):
3407         schema = (("whatever", Boolean()),)
3408     base_klass = Wahl
3409
3410     def test_schema_required(self):
3411         with assertRaisesRegex(self, ValueError, "schema must be specified"):
3412             Choice()
3413
3414     def test_impl_forbidden(self):
3415         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
3416             Choice(impl=b"whatever")
3417
3418     def test_invalid_value_type(self):
3419         with self.assertRaises(InvalidValueType) as err:
3420             self.base_klass(123)
3421         repr(err.exception)
3422         with self.assertRaises(ObjUnknown) as err:
3423             self.base_klass(("whenever", Boolean(False)))
3424         repr(err.exception)
3425         with self.assertRaises(InvalidValueType) as err:
3426             self.base_klass(("whatever", Integer(123)))
3427         repr(err.exception)
3428
3429     @given(booleans())
3430     def test_optional(self, optional):
3431         obj = self.base_klass(
3432             default=self.base_klass(("whatever", Boolean(False))),
3433             optional=optional,
3434         )
3435         self.assertTrue(obj.optional)
3436
3437     @given(booleans())
3438     def test_ready(self, value):
3439         obj = self.base_klass()
3440         self.assertFalse(obj.ready)
3441         repr(obj)
3442         pprint(obj)
3443         self.assertIsNone(obj["whatever"])
3444         with self.assertRaises(ObjNotReady) as err:
3445             obj.encode()
3446         repr(err.exception)
3447         obj["whatever"] = Boolean()
3448         self.assertFalse(obj.ready)
3449         repr(obj)
3450         pprint(obj)
3451         obj["whatever"] = Boolean(value)
3452         self.assertTrue(obj.ready)
3453         repr(obj)
3454         pprint(obj)
3455
3456     @given(booleans(), booleans())
3457     def test_comparison(self, value1, value2):
3458         class WahlInherited(self.base_klass):
3459             pass
3460         for klass in (self.base_klass, WahlInherited):
3461             obj1 = klass(("whatever", Boolean(value1)))
3462             obj2 = klass(("whatever", Boolean(value2)))
3463             self.assertEqual(obj1 == obj2, value1 == value2)
3464             self.assertEqual(obj1 != obj2, value1 != value2)
3465             self.assertEqual(obj1 == obj2._value, value1 == value2)
3466             self.assertFalse(obj1 == obj2._value[1])
3467
3468     @given(data_strategy())
3469     def test_call(self, d):
3470         for klass in (Choice, ChoiceInherited):
3471             (
3472                 schema_initial,
3473                 value_initial,
3474                 expl_initial,
3475                 default_initial,
3476                 optional_initial,
3477                 _decoded_initial,
3478             ) = d.draw(choice_values_strategy())
3479
3480             class Wahl(klass):
3481                 schema = schema_initial
3482             obj_initial = Wahl(
3483                 value=value_initial,
3484                 expl=expl_initial,
3485                 default=default_initial,
3486                 optional=optional_initial or False,
3487                 _decoded=_decoded_initial,
3488             )
3489             (
3490                 _,
3491                 value,
3492                 expl,
3493                 default,
3494                 optional,
3495                 _decoded,
3496             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
3497             obj = obj_initial(value, expl, default, optional)
3498             if obj.ready:
3499                 value_expected = default if value is None else value
3500                 value_expected = (
3501                     default_initial if value_expected is None
3502                     else value_expected
3503                 )
3504                 self.assertEqual(obj.choice, value_expected[0])
3505                 self.assertEqual(obj.value, int(value_expected[1]))
3506             self.assertEqual(obj.expl_tag, expl or expl_initial)
3507             default_expect = default_initial if default is None else default
3508             if default_expect is not None:
3509                 self.assertEqual(obj.default.choice, default_expect[0])
3510                 self.assertEqual(obj.default.value, int(default_expect[1]))
3511             if obj.default is None:
3512                 optional = optional_initial if optional is None else optional
3513                 optional = False if optional is None else optional
3514             else:
3515                 optional = True
3516             self.assertEqual(obj.optional, optional)
3517             self.assertEqual(obj.specs, obj_initial.specs)
3518
3519     def test_simultaneous_impl_expl(self):
3520         # override it, as Any does not have implicit tag
3521         pass
3522
3523     def test_decoded(self):
3524         # override it, as Any does not have implicit tag
3525         pass
3526
3527     @given(choice_values_strategy())
3528     def test_copy(self, values):
3529         _schema, value, expl, default, optional, _decoded = values
3530
3531         class Wahl(self.base_klass):
3532             schema = _schema
3533         obj = Wahl(
3534             value=value,
3535             expl=expl,
3536             default=default,
3537             optional=optional or False,
3538             _decoded=_decoded,
3539         )
3540         obj_copied = obj.copy()
3541         self.assertIsNone(obj.tag)
3542         self.assertIsNone(obj_copied.tag)
3543         # hack for assert_copied_basic_fields
3544         obj.tag = "whatever"
3545         obj_copied.tag = "whatever"
3546         self.assert_copied_basic_fields(obj, obj_copied)
3547         self.assertEqual(obj._value, obj_copied._value)
3548         self.assertEqual(obj.specs, obj_copied.specs)
3549
3550     @given(booleans())
3551     def test_stripped(self, value):
3552         obj = self.base_klass(("whatever", Boolean(value)))
3553         with self.assertRaises(NotEnoughData):
3554             obj.decode(obj.encode()[:-1])
3555
3556     @given(
3557         booleans(),
3558         integers(min_value=1).map(tag_ctxc),
3559     )
3560     def test_stripped_expl(self, value, tag_expl):
3561         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
3562         with self.assertRaises(NotEnoughData):
3563             obj.decode(obj.encode()[:-1])
3564
3565     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3566     @given(data_strategy())
3567     def test_symmetric(self, d):
3568         _schema, value, _, default, optional, _decoded = d.draw(
3569             choice_values_strategy(value_required=True)
3570         )
3571         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3572         offset = d.draw(integers(min_value=0))
3573
3574         class Wahl(self.base_klass):
3575             schema = _schema
3576         obj = Wahl(
3577             value=value,
3578             default=default,
3579             optional=optional,
3580             _decoded=_decoded,
3581         )
3582         repr(obj)
3583         pprint(obj)
3584         self.assertFalse(obj.expled)
3585         obj_encoded = obj.encode()
3586         obj_expled = obj(value, expl=tag_expl)
3587         self.assertTrue(obj_expled.expled)
3588         repr(obj_expled)
3589         pprint(obj_expled)
3590         obj_expled_encoded = obj_expled.encode()
3591         obj_decoded, tail = obj_expled.decode(obj_expled_encoded, offset=offset)
3592         repr(obj_decoded)
3593         pprint(obj_decoded)
3594         self.assertEqual(tail, b"")
3595         self.assertEqual(obj_decoded, obj_expled)
3596         self.assertEqual(obj_decoded.choice, obj_expled.choice)
3597         self.assertEqual(obj_decoded.value, obj_expled.value)
3598         self.assertEqual(obj_decoded.choice, obj.choice)
3599         self.assertEqual(obj_decoded.value, obj.value)
3600         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3601         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3602         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3603         self.assertEqual(
3604             obj_decoded.expl_llen,
3605             len(len_encode(len(obj_encoded))),
3606         )
3607         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3608         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3609         self.assertEqual(
3610             obj_decoded.offset,
3611             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3612         )
3613         self.assertEqual(obj_decoded.expl_offset, offset)
3614         self.assertSequenceEqual(
3615             obj_expled_encoded[
3616                 obj_decoded.value.offset - offset:
3617                 obj_decoded.value.offset + obj_decoded.value.tlvlen - offset
3618             ],
3619             obj_encoded,
3620         )
3621
3622     @given(integers())
3623     def test_set_get(self, value):
3624         class Wahl(Choice):
3625             schema = (
3626                 ("erste", Boolean()),
3627                 ("zweite", Integer()),
3628             )
3629         obj = Wahl()
3630         with self.assertRaises(ObjUnknown) as err:
3631             obj["whatever"] = "whenever"
3632         with self.assertRaises(InvalidValueType) as err:
3633             obj["zweite"] = Boolean(False)
3634         obj["zweite"] = Integer(value)
3635         repr(err.exception)
3636         with self.assertRaises(ObjUnknown) as err:
3637             obj["whatever"]
3638         repr(err.exception)
3639         self.assertIsNone(obj["erste"])
3640         self.assertEqual(obj["zweite"], Integer(value))
3641
3642     def test_tag_mismatch(self):
3643         class Wahl(Choice):
3644             schema = (
3645                 ("erste", Boolean()),
3646             )
3647         int_encoded = Integer(123).encode()
3648         bool_encoded = Boolean(False).encode()
3649         obj = Wahl()
3650         obj.decode(bool_encoded)
3651         with self.assertRaises(TagMismatch):
3652             obj.decode(int_encoded)
3653
3654
3655 @composite
3656 def seq_values_strategy(draw, seq_klass, do_expl=False):
3657     value = None
3658     if draw(booleans()):
3659         value = seq_klass()
3660         value._value = {
3661             k: v for k, v in draw(dictionaries(
3662                 integers(),
3663                 one_of(
3664                     booleans().map(Boolean),
3665                     integers().map(Integer),
3666                 ),
3667             )).items()
3668         }
3669     schema = None
3670     if draw(booleans()):
3671         schema = list(draw(dictionaries(
3672             integers(),
3673             one_of(
3674                 booleans().map(Boolean),
3675                 integers().map(Integer),
3676             ),
3677         )).items())
3678     impl = None
3679     expl = None
3680     if do_expl:
3681         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3682     else:
3683         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3684     default = None
3685     if draw(booleans()):
3686         default = seq_klass()
3687         default._value = {
3688             k: v for k, v in draw(dictionaries(
3689                 integers(),
3690                 one_of(
3691                     booleans().map(Boolean),
3692                     integers().map(Integer),
3693                 ),
3694             )).items()
3695         }
3696     optional = draw(one_of(none(), booleans()))
3697     _decoded = (
3698         draw(integers(min_value=0)),
3699         draw(integers(min_value=0)),
3700         draw(integers(min_value=0)),
3701     )
3702     return (value, schema, impl, expl, default, optional, _decoded)
3703
3704
3705 @composite
3706 def sequence_strategy(draw, seq_klass):
3707     inputs = draw(lists(
3708         one_of(
3709             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
3710             tuples(just(Integer), integers(), one_of(none(), integers())),
3711         ),
3712         max_size=6,
3713     ))
3714     tags = draw(sets(
3715         integers(min_value=1),
3716         min_size=len(inputs),
3717         max_size=len(inputs),
3718     ))
3719     inits = [
3720         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
3721         for tag, expled in zip(tags, draw(lists(
3722             booleans(),
3723             min_size=len(inputs),
3724             max_size=len(inputs),
3725         )))
3726     ]
3727     empties = []
3728     for i, optional in enumerate(draw(lists(
3729             sampled_from(("required", "optional", "empty")),
3730             min_size=len(inputs),
3731             max_size=len(inputs),
3732     ))):
3733         if optional in ("optional", "empty"):
3734             inits[i]["optional"] = True
3735         if optional == "empty":
3736             empties.append(i)
3737     empties = set(empties)
3738     names = list(draw(sets(
3739         text_printable,
3740         min_size=len(inputs),
3741         max_size=len(inputs),
3742     )))
3743     schema = []
3744     for i, (klass, value, default) in enumerate(inputs):
3745         schema.append((names[i], klass(default=default, **inits[i])))
3746     seq_name = draw(text_letters())
3747     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
3748     seq = Seq()
3749     expects = []
3750     for i, (klass, value, default) in enumerate(inputs):
3751         name = names[i]
3752         _, spec = schema[i]
3753         expect = {
3754             "name": name,
3755             "optional": False,
3756             "presented": False,
3757             "default_value": None if spec.default is None else default,
3758             "value": None,
3759         }
3760         if i in empties:
3761             expect["optional"] = True
3762         else:
3763             expect["presented"] = True
3764             expect["value"] = value
3765             if spec.optional:
3766                 expect["optional"] = True
3767             if default is not None and default == value:
3768                 expect["presented"] = False
3769             seq[name] = klass(value)
3770         expects.append(expect)
3771     return seq, expects
3772
3773
3774 @composite
3775 def sequences_strategy(draw, seq_klass):
3776     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
3777     inits = [
3778         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
3779         for tag, expled in zip(tags, draw(lists(
3780             booleans(),
3781             min_size=len(tags),
3782             max_size=len(tags),
3783         )))
3784     ]
3785     defaulted = set(
3786         i for i, is_default in enumerate(draw(lists(
3787             booleans(),
3788             min_size=len(tags),
3789             max_size=len(tags),
3790         ))) if is_default
3791     )
3792     names = list(draw(sets(
3793         text_printable,
3794         min_size=len(tags),
3795         max_size=len(tags),
3796     )))
3797     seq_expectses = draw(lists(
3798         sequence_strategy(seq_klass=seq_klass),
3799         min_size=len(tags),
3800         max_size=len(tags),
3801     ))
3802     seqs = [seq for seq, _ in seq_expectses]
3803     schema = []
3804     for i, (name, seq) in enumerate(zip(names, seqs)):
3805         schema.append((
3806             name,
3807             seq(default=(seq if i in defaulted else None), **inits[i]),
3808         ))
3809     seq_name = draw(text_letters())
3810     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
3811     seq_outer = Seq()
3812     expect_outers = []
3813     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
3814         expect = {
3815             "name": name,
3816             "expects": expects_inner,
3817             "presented": False,
3818         }
3819         seq_outer[name] = seq_inner
3820         if seq_outer.specs[name].default is None:
3821             expect["presented"] = True
3822         expect_outers.append(expect)
3823     return seq_outer, expect_outers
3824
3825
3826 class SeqMixing(object):
3827     def test_invalid_value_type(self):
3828         with self.assertRaises(InvalidValueType) as err:
3829             self.base_klass((1, 2, 3))
3830         repr(err.exception)
3831
3832     def test_invalid_value_type_set(self):
3833         class Seq(self.base_klass):
3834             schema = (("whatever", Boolean()),)
3835         seq = Seq()
3836         with self.assertRaises(InvalidValueType) as err:
3837             seq["whatever"] = Integer(123)
3838         repr(err.exception)
3839
3840     @given(booleans())
3841     def test_optional(self, optional):
3842         obj = self.base_klass(default=self.base_klass(), optional=optional)
3843         self.assertTrue(obj.optional)
3844
3845     @given(data_strategy())
3846     def test_ready(self, d):
3847         ready = {
3848             str(i): v for i, v in enumerate(d.draw(lists(
3849                 booleans(),
3850                 min_size=1,
3851                 max_size=3,
3852             )))
3853         }
3854         non_ready = {
3855             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
3856                 booleans(),
3857                 min_size=1,
3858                 max_size=3,
3859             )))
3860         }
3861         schema_input = []
3862         for name in d.draw(permutations(
3863                 list(ready.keys()) + list(non_ready.keys()),
3864         )):
3865             schema_input.append((name, Boolean()))
3866
3867         class Seq(self.base_klass):
3868             schema = tuple(schema_input)
3869         seq = Seq()
3870         for name in ready.keys():
3871             seq[name]
3872             seq[name] = Boolean()
3873         self.assertFalse(seq.ready)
3874         repr(seq)
3875         pprint(seq)
3876         for name, value in ready.items():
3877             seq[name] = Boolean(value)
3878         self.assertFalse(seq.ready)
3879         repr(seq)
3880         pprint(seq)
3881         with self.assertRaises(ObjNotReady) as err:
3882             seq.encode()
3883         repr(err.exception)
3884         for name, value in non_ready.items():
3885             seq[name] = Boolean(value)
3886         self.assertTrue(seq.ready)
3887         repr(seq)
3888         pprint(seq)
3889
3890     @given(data_strategy())
3891     def test_call(self, d):
3892         class SeqInherited(self.base_klass):
3893             pass
3894         for klass in (self.base_klass, SeqInherited):
3895             (
3896                 value_initial,
3897                 schema_initial,
3898                 impl_initial,
3899                 expl_initial,
3900                 default_initial,
3901                 optional_initial,
3902                 _decoded_initial,
3903             ) = d.draw(seq_values_strategy(seq_klass=klass))
3904             obj_initial = klass(
3905                 value_initial,
3906                 schema_initial,
3907                 impl_initial,
3908                 expl_initial,
3909                 default_initial,
3910                 optional_initial or False,
3911                 _decoded_initial,
3912             )
3913             (
3914                 value,
3915                 _,
3916                 impl,
3917                 expl,
3918                 default,
3919                 optional,
3920                 _decoded,
3921             ) = d.draw(seq_values_strategy(
3922                 seq_klass=klass,
3923                 do_expl=impl_initial is None,
3924             ))
3925             obj = obj_initial(value, impl, expl, default, optional)
3926             value_expected = default if value is None else value
3927             value_expected = (
3928                 default_initial if value_expected is None
3929                 else value_expected
3930             )
3931             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
3932             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
3933             self.assertEqual(obj.expl_tag, expl or expl_initial)
3934             self.assertEqual(
3935                 {} if obj.default is None else obj.default._value,
3936                 getattr(default_initial if default is None else default, "_value", {}),
3937             )
3938             if obj.default is None:
3939                 optional = optional_initial if optional is None else optional
3940                 optional = False if optional is None else optional
3941             else:
3942                 optional = True
3943             self.assertEqual(list(obj.specs.items()), schema_initial or [])
3944             self.assertEqual(obj.optional, optional)
3945
3946     @given(data_strategy())
3947     def test_copy(self, d):
3948         class SeqInherited(self.base_klass):
3949             pass
3950         for klass in (self.base_klass, SeqInherited):
3951             values = d.draw(seq_values_strategy(seq_klass=klass))
3952             obj = klass(*values)
3953             obj_copied = obj.copy()
3954             self.assert_copied_basic_fields(obj, obj_copied)
3955             self.assertEqual(obj.specs, obj_copied.specs)
3956             self.assertEqual(obj._value, obj_copied._value)
3957
3958     @given(data_strategy())
3959     def test_stripped(self, d):
3960         value = d.draw(integers())
3961         tag_impl = tag_encode(d.draw(integers(min_value=1)))
3962
3963         class Seq(self.base_klass):
3964             impl = tag_impl
3965             schema = (("whatever", Integer()),)
3966         seq = Seq()
3967         seq["whatever"] = Integer(value)
3968         with self.assertRaises(NotEnoughData):
3969             seq.decode(seq.encode()[:-1])
3970
3971     @given(data_strategy())
3972     def test_stripped_expl(self, d):
3973         value = d.draw(integers())
3974         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
3975
3976         class Seq(self.base_klass):
3977             expl = tag_expl
3978             schema = (("whatever", Integer()),)
3979         seq = Seq()
3980         seq["whatever"] = Integer(value)
3981         with self.assertRaises(NotEnoughData):
3982             seq.decode(seq.encode()[:-1])
3983
3984     @given(binary(min_size=2))
3985     def test_non_tag_mismatch_raised(self, junk):
3986         try:
3987             _, _, len_encoded = tag_strip(memoryview(junk))
3988             len_decode(len_encoded)
3989         except Exception:
3990             assume(True)
3991         else:
3992             assume(False)
3993
3994         class Seq(self.base_klass):
3995             schema = (
3996                 ("whatever", Integer()),
3997                 ("junk", Any()),
3998                 ("whenever", Integer()),
3999             )
4000         seq = Seq()
4001         seq["whatever"] = Integer(123)
4002         seq["junk"] = Any(junk)
4003         seq["whenever"] = Integer(123)
4004         with self.assertRaises(DecodeError):
4005             seq.decode(seq.encode())
4006
4007     @given(
4008         integers(min_value=31),
4009         integers(min_value=0),
4010         lists(integers()),
4011     )
4012     def test_bad_tag(self, tag, offset, decode_path):
4013         decode_path = tuple(str(i) for i in decode_path)
4014         with self.assertRaises(DecodeError) as err:
4015             self.base_klass().decode(
4016                 tag_encode(tag)[:-1],
4017                 offset=offset,
4018                 decode_path=decode_path,
4019             )
4020         repr(err.exception)
4021         self.assertEqual(err.exception.offset, offset)
4022         self.assertEqual(err.exception.decode_path, decode_path)
4023
4024     @given(
4025         integers(min_value=128),
4026         integers(min_value=0),
4027         lists(integers()),
4028     )
4029     def test_bad_len(self, l, offset, decode_path):
4030         decode_path = tuple(str(i) for i in decode_path)
4031         with self.assertRaises(DecodeError) as err:
4032             self.base_klass().decode(
4033                 self.base_klass.tag_default + len_encode(l)[:-1],
4034                 offset=offset,
4035                 decode_path=decode_path,
4036             )
4037         repr(err.exception)
4038         self.assertEqual(err.exception.offset, offset)
4039         self.assertEqual(err.exception.decode_path, decode_path)
4040
4041     def _assert_expects(self, seq, expects):
4042         for expect in expects:
4043             self.assertEqual(
4044                 seq.specs[expect["name"]].optional,
4045                 expect["optional"],
4046             )
4047             if expect["default_value"] is not None:
4048                 self.assertEqual(
4049                     seq.specs[expect["name"]].default,
4050                     expect["default_value"],
4051                 )
4052             if expect["presented"]:
4053                 self.assertIn(expect["name"], seq)
4054                 self.assertEqual(seq[expect["name"]], expect["value"])
4055
4056     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4057     @given(data_strategy())
4058     def test_symmetric(self, d):
4059         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
4060         self.assertTrue(seq.ready)
4061         self.assertFalse(seq.decoded)
4062         self._assert_expects(seq, expects)
4063         repr(seq)
4064         pprint(seq)
4065         seq_encoded = seq.encode()
4066         seq_decoded, tail = seq.decode(seq_encoded)
4067         self.assertEqual(tail, b"")
4068         self.assertTrue(seq.ready)
4069         self._assert_expects(seq_decoded, expects)
4070         self.assertEqual(seq, seq_decoded)
4071         self.assertEqual(seq_decoded.encode(), seq_encoded)
4072         for expect in expects:
4073             if not expect["presented"]:
4074                 self.assertNotIn(expect["name"], seq_decoded)
4075                 continue
4076             self.assertIn(expect["name"], seq_decoded)
4077             obj = seq_decoded[expect["name"]]
4078             self.assertTrue(obj.decoded)
4079             offset = obj.expl_offset if obj.expled else obj.offset
4080             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4081             self.assertSequenceEqual(
4082                 seq_encoded[offset:offset + tlvlen],
4083                 obj.encode(),
4084             )
4085
4086     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4087     @given(data_strategy())
4088     def test_symmetric_with_seq(self, d):
4089         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
4090         self.assertTrue(seq.ready)
4091         seq_encoded = seq.encode()
4092         seq_decoded, tail = seq.decode(seq_encoded)
4093         self.assertEqual(tail, b"")
4094         self.assertTrue(seq.ready)
4095         self.assertEqual(seq, seq_decoded)
4096         self.assertEqual(seq_decoded.encode(), seq_encoded)
4097         for expect_outer in expect_outers:
4098             if not expect_outer["presented"]:
4099                 self.assertNotIn(expect_outer["name"], seq_decoded)
4100                 continue
4101             self.assertIn(expect_outer["name"], seq_decoded)
4102             obj = seq_decoded[expect_outer["name"]]
4103             self.assertTrue(obj.decoded)
4104             offset = obj.expl_offset if obj.expled else obj.offset
4105             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4106             self.assertSequenceEqual(
4107                 seq_encoded[offset:offset + tlvlen],
4108                 obj.encode(),
4109             )
4110             self._assert_expects(obj, expect_outer["expects"])
4111
4112     @given(data_strategy())
4113     def test_default_disappears(self, d):
4114         _schema = list(d.draw(dictionaries(
4115             text_letters(),
4116             sets(integers(), min_size=2, max_size=2),
4117             min_size=1,
4118         )).items())
4119
4120         class Seq(self.base_klass):
4121             schema = [
4122                 (n, Integer(default=d))
4123                 for n, (_, d) in _schema
4124             ]
4125         seq = Seq()
4126         for name, (value, _) in _schema:
4127             seq[name] = Integer(value)
4128         self.assertEqual(len(seq._value), len(_schema))
4129         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
4130         self.assertGreater(len(seq.encode()), len(empty_seq))
4131         for name, (_, default) in _schema:
4132             seq[name] = Integer(default)
4133         self.assertEqual(len(seq._value), 0)
4134         self.assertSequenceEqual(seq.encode(), empty_seq)
4135
4136     @given(data_strategy())
4137     def test_encoded_default_accepted(self, d):
4138         _schema = list(d.draw(dictionaries(
4139             text_letters(),
4140             integers(),
4141             min_size=1,
4142         )).items())
4143         tags = [tag_encode(tag) for tag in d.draw(sets(
4144             integers(min_value=0),
4145             min_size=len(_schema),
4146             max_size=len(_schema),
4147         ))]
4148
4149         class SeqWithoutDefault(self.base_klass):
4150             schema = [
4151                 (n, Integer(impl=t))
4152                 for (n, _), t in zip(_schema, tags)
4153             ]
4154         seq_without_default = SeqWithoutDefault()
4155         for name, value in _schema:
4156             seq_without_default[name] = Integer(value)
4157         seq_encoded = seq_without_default.encode()
4158
4159         class SeqWithDefault(self.base_klass):
4160             schema = [
4161                 (n, Integer(default=v, impl=t))
4162                 for (n, v), t in zip(_schema, tags)
4163             ]
4164         seq_with_default = SeqWithDefault()
4165         seq_decoded, _ = seq_with_default.decode(seq_encoded)
4166         for name, value in _schema:
4167             self.assertEqual(seq_decoded[name], seq_with_default[name])
4168             self.assertEqual(seq_decoded[name], value)
4169
4170     @given(data_strategy())
4171     def test_missing_from_spec(self, d):
4172         names = list(d.draw(sets(text_letters(), min_size=2)))
4173         tags = [tag_encode(tag) for tag in d.draw(sets(
4174             integers(min_value=0),
4175             min_size=len(names),
4176             max_size=len(names),
4177         ))]
4178         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
4179
4180         class SeqFull(self.base_klass):
4181             schema = [(n, Integer(impl=t)) for n, t in names_tags]
4182         seq_full = SeqFull()
4183         for i, name in enumerate(names):
4184             seq_full[name] = Integer(i)
4185         seq_encoded = seq_full.encode()
4186         altered = names_tags[:-2] + names_tags[-1:]
4187
4188         class SeqMissing(self.base_klass):