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