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