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