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