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