]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
NumericString sanitizes its value against numbers
[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(), 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())
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(),
1142         binary(),
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(), binary())
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())
2559         tag2 = d.draw(binary())
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())
2997         tag2 = d.draw(binary())
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
3776 @composite
3777 def seq_values_strategy(draw, seq_klass, do_expl=False):
3778     value = None
3779     if draw(booleans()):
3780         value = seq_klass()
3781         value._value = {
3782             k: v for k, v in draw(dictionaries(
3783                 integers(),
3784                 one_of(
3785                     booleans().map(Boolean),
3786                     integers().map(Integer),
3787                 ),
3788             )).items()
3789         }
3790     schema = None
3791     if draw(booleans()):
3792         schema = list(draw(dictionaries(
3793             integers(),
3794             one_of(
3795                 booleans().map(Boolean),
3796                 integers().map(Integer),
3797             ),
3798         )).items())
3799     impl = None
3800     expl = None
3801     if do_expl:
3802         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3803     else:
3804         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3805     default = None
3806     if draw(booleans()):
3807         default = seq_klass()
3808         default._value = {
3809             k: v for k, v in draw(dictionaries(
3810                 integers(),
3811                 one_of(
3812                     booleans().map(Boolean),
3813                     integers().map(Integer),
3814                 ),
3815             )).items()
3816         }
3817     optional = draw(one_of(none(), booleans()))
3818     _decoded = (
3819         draw(integers(min_value=0)),
3820         draw(integers(min_value=0)),
3821         draw(integers(min_value=0)),
3822     )
3823     return (value, schema, impl, expl, default, optional, _decoded)
3824
3825
3826 @composite
3827 def sequence_strategy(draw, seq_klass):
3828     inputs = draw(lists(
3829         one_of(
3830             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
3831             tuples(just(Integer), integers(), one_of(none(), integers())),
3832         ),
3833         max_size=6,
3834     ))
3835     tags = draw(sets(
3836         integers(min_value=1),
3837         min_size=len(inputs),
3838         max_size=len(inputs),
3839     ))
3840     inits = [
3841         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
3842         for tag, expled in zip(tags, draw(lists(
3843             booleans(),
3844             min_size=len(inputs),
3845             max_size=len(inputs),
3846         )))
3847     ]
3848     empties = []
3849     for i, optional in enumerate(draw(lists(
3850             sampled_from(("required", "optional", "empty")),
3851             min_size=len(inputs),
3852             max_size=len(inputs),
3853     ))):
3854         if optional in ("optional", "empty"):
3855             inits[i]["optional"] = True
3856         if optional == "empty":
3857             empties.append(i)
3858     empties = set(empties)
3859     names = list(draw(sets(
3860         text_printable,
3861         min_size=len(inputs),
3862         max_size=len(inputs),
3863     )))
3864     schema = []
3865     for i, (klass, value, default) in enumerate(inputs):
3866         schema.append((names[i], klass(default=default, **inits[i])))
3867     seq_name = draw(text_letters())
3868     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
3869     seq = Seq()
3870     expects = []
3871     for i, (klass, value, default) in enumerate(inputs):
3872         name = names[i]
3873         _, spec = schema[i]
3874         expect = {
3875             "name": name,
3876             "optional": False,
3877             "presented": False,
3878             "default_value": None if spec.default is None else default,
3879             "value": None,
3880         }
3881         if i in empties:
3882             expect["optional"] = True
3883         else:
3884             expect["presented"] = True
3885             expect["value"] = value
3886             if spec.optional:
3887                 expect["optional"] = True
3888             if default is not None and default == value:
3889                 expect["presented"] = False
3890             seq[name] = klass(value)
3891         expects.append(expect)
3892     return seq, expects
3893
3894
3895 @composite
3896 def sequences_strategy(draw, seq_klass):
3897     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
3898     inits = [
3899         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
3900         for tag, expled in zip(tags, draw(lists(
3901             booleans(),
3902             min_size=len(tags),
3903             max_size=len(tags),
3904         )))
3905     ]
3906     defaulted = set(
3907         i for i, is_default in enumerate(draw(lists(
3908             booleans(),
3909             min_size=len(tags),
3910             max_size=len(tags),
3911         ))) if is_default
3912     )
3913     names = list(draw(sets(
3914         text_printable,
3915         min_size=len(tags),
3916         max_size=len(tags),
3917     )))
3918     seq_expectses = draw(lists(
3919         sequence_strategy(seq_klass=seq_klass),
3920         min_size=len(tags),
3921         max_size=len(tags),
3922     ))
3923     seqs = [seq for seq, _ in seq_expectses]
3924     schema = []
3925     for i, (name, seq) in enumerate(zip(names, seqs)):
3926         schema.append((
3927             name,
3928             seq(default=(seq if i in defaulted else None), **inits[i]),
3929         ))
3930     seq_name = draw(text_letters())
3931     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
3932     seq_outer = Seq()
3933     expect_outers = []
3934     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
3935         expect = {
3936             "name": name,
3937             "expects": expects_inner,
3938             "presented": False,
3939         }
3940         seq_outer[name] = seq_inner
3941         if seq_outer.specs[name].default is None:
3942             expect["presented"] = True
3943         expect_outers.append(expect)
3944     return seq_outer, expect_outers
3945
3946
3947 class SeqMixing(object):
3948     def test_invalid_value_type(self):
3949         with self.assertRaises(InvalidValueType) as err:
3950             self.base_klass((1, 2, 3))
3951         repr(err.exception)
3952
3953     def test_invalid_value_type_set(self):
3954         class Seq(self.base_klass):
3955             schema = (("whatever", Boolean()),)
3956         seq = Seq()
3957         with self.assertRaises(InvalidValueType) as err:
3958             seq["whatever"] = Integer(123)
3959         repr(err.exception)
3960
3961     @given(booleans())
3962     def test_optional(self, optional):
3963         obj = self.base_klass(default=self.base_klass(), optional=optional)
3964         self.assertTrue(obj.optional)
3965
3966     @given(data_strategy())
3967     def test_ready(self, d):
3968         ready = {
3969             str(i): v for i, v in enumerate(d.draw(lists(
3970                 booleans(),
3971                 min_size=1,
3972                 max_size=3,
3973             )))
3974         }
3975         non_ready = {
3976             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
3977                 booleans(),
3978                 min_size=1,
3979                 max_size=3,
3980             )))
3981         }
3982         schema_input = []
3983         for name in d.draw(permutations(
3984                 list(ready.keys()) + list(non_ready.keys()),
3985         )):
3986             schema_input.append((name, Boolean()))
3987
3988         class Seq(self.base_klass):
3989             schema = tuple(schema_input)
3990         seq = Seq()
3991         for name in ready.keys():
3992             seq[name]
3993             seq[name] = Boolean()
3994         self.assertFalse(seq.ready)
3995         repr(seq)
3996         pprint(seq)
3997         for name, value in ready.items():
3998             seq[name] = Boolean(value)
3999         self.assertFalse(seq.ready)
4000         repr(seq)
4001         pprint(seq)
4002         with self.assertRaises(ObjNotReady) as err:
4003             seq.encode()
4004         repr(err.exception)
4005         for name, value in non_ready.items():
4006             seq[name] = Boolean(value)
4007         self.assertTrue(seq.ready)
4008         repr(seq)
4009         pprint(seq)
4010
4011     @given(data_strategy())
4012     def test_call(self, d):
4013         class SeqInherited(self.base_klass):
4014             pass
4015         for klass in (self.base_klass, SeqInherited):
4016             (
4017                 value_initial,
4018                 schema_initial,
4019                 impl_initial,
4020                 expl_initial,
4021                 default_initial,
4022                 optional_initial,
4023                 _decoded_initial,
4024             ) = d.draw(seq_values_strategy(seq_klass=klass))
4025             obj_initial = klass(
4026                 value_initial,
4027                 schema_initial,
4028                 impl_initial,
4029                 expl_initial,
4030                 default_initial,
4031                 optional_initial or False,
4032                 _decoded_initial,
4033             )
4034             (
4035                 value,
4036                 _,
4037                 impl,
4038                 expl,
4039                 default,
4040                 optional,
4041                 _decoded,
4042             ) = d.draw(seq_values_strategy(
4043                 seq_klass=klass,
4044                 do_expl=impl_initial is None,
4045             ))
4046             obj = obj_initial(value, impl, expl, default, optional)
4047             value_expected = default if value is None else value
4048             value_expected = (
4049                 default_initial if value_expected is None
4050                 else value_expected
4051             )
4052             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
4053             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4054             self.assertEqual(obj.expl_tag, expl or expl_initial)
4055             self.assertEqual(
4056                 {} if obj.default is None else obj.default._value,
4057                 getattr(default_initial if default is None else default, "_value", {}),
4058             )
4059             if obj.default is None:
4060                 optional = optional_initial if optional is None else optional
4061                 optional = False if optional is None else optional
4062             else:
4063                 optional = True
4064             self.assertEqual(list(obj.specs.items()), schema_initial or [])
4065             self.assertEqual(obj.optional, optional)
4066
4067     @given(data_strategy())
4068     def test_copy(self, d):
4069         class SeqInherited(self.base_klass):
4070             pass
4071         for klass in (self.base_klass, SeqInherited):
4072             values = d.draw(seq_values_strategy(seq_klass=klass))
4073             obj = klass(*values)
4074             obj_copied = obj.copy()
4075             self.assert_copied_basic_fields(obj, obj_copied)
4076             self.assertEqual(obj.specs, obj_copied.specs)
4077             self.assertEqual(obj._value, obj_copied._value)
4078
4079     @given(data_strategy())
4080     def test_stripped(self, d):
4081         value = d.draw(integers())
4082         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4083
4084         class Seq(self.base_klass):
4085             impl = tag_impl
4086             schema = (("whatever", Integer()),)
4087         seq = Seq()
4088         seq["whatever"] = Integer(value)
4089         with self.assertRaises(NotEnoughData):
4090             seq.decode(seq.encode()[:-1])
4091
4092     @given(data_strategy())
4093     def test_stripped_expl(self, d):
4094         value = d.draw(integers())
4095         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4096
4097         class Seq(self.base_klass):
4098             expl = tag_expl
4099             schema = (("whatever", Integer()),)
4100         seq = Seq()
4101         seq["whatever"] = Integer(value)
4102         with self.assertRaises(NotEnoughData):
4103             seq.decode(seq.encode()[:-1])
4104
4105     @given(binary(min_size=2))
4106     def test_non_tag_mismatch_raised(self, junk):
4107         try:
4108             _, _, len_encoded = tag_strip(memoryview(junk))
4109             len_decode(len_encoded)
4110         except Exception:
4111             assume(True)
4112         else:
4113             assume(False)
4114
4115         class Seq(self.base_klass):
4116             schema = (
4117                 ("whatever", Integer()),
4118                 ("junk", Any()),
4119                 ("whenever", Integer()),
4120             )
4121         seq = Seq()
4122         seq["whatever"] = Integer(123)
4123         seq["junk"] = Any(junk)
4124         seq["whenever"] = Integer(123)
4125         with self.assertRaises(DecodeError):
4126             seq.decode(seq.encode())
4127
4128     @given(
4129         integers(min_value=31),
4130         integers(min_value=0),
4131         lists(integers()),
4132     )
4133     def test_bad_tag(self, tag, offset, decode_path):
4134         decode_path = tuple(str(i) for i in decode_path)
4135         with self.assertRaises(DecodeError) as err:
4136             self.base_klass().decode(
4137                 tag_encode(tag)[:-1],
4138                 offset=offset,
4139                 decode_path=decode_path,
4140             )
4141         repr(err.exception)
4142         self.assertEqual(err.exception.offset, offset)
4143         self.assertEqual(err.exception.decode_path, decode_path)
4144
4145     @given(
4146         integers(min_value=128),
4147         integers(min_value=0),
4148         lists(integers()),
4149     )
4150     def test_bad_len(self, l, offset, decode_path):
4151         decode_path = tuple(str(i) for i in decode_path)
4152         with self.assertRaises(DecodeError) as err:
4153             self.base_klass().decode(
4154                 self.base_klass.tag_default + len_encode(l)[:-1],
4155                 offset=offset,
4156                 decode_path=decode_path,
4157             )
4158         repr(err.exception)
4159         self.assertEqual(err.exception.offset, offset)
4160         self.assertEqual(err.exception.decode_path, decode_path)
4161
4162     def _assert_expects(self, seq, expects):
4163         for expect in expects:
4164             self.assertEqual(
4165                 seq.specs[expect["name"]].optional,
4166                 expect["optional"],
4167             )
4168             if expect["default_value"] is not None:
4169                 self.assertEqual(
4170                     seq.specs[expect["name"]].default,
4171                     expect["default_value"],
4172                 )
4173             if expect["presented"]:
4174                 self.assertIn(expect["name"], seq)
4175                 self.assertEqual(seq[expect["name"]], expect["value"])
4176
4177     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4178     @given(data_strategy())
4179     def test_symmetric(self, d):
4180         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
4181         tail_junk = d.draw(binary(max_size=5))
4182         self.assertTrue(seq.ready)
4183         self.assertFalse(seq.decoded)
4184         self._assert_expects(seq, expects)
4185         repr(seq)
4186         pprint(seq)
4187         seq_encoded = seq.encode()
4188         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
4189         self.assertEqual(tail, tail_junk)
4190         self.assertTrue(seq.ready)
4191         self._assert_expects(seq_decoded, expects)
4192         self.assertEqual(seq, seq_decoded)
4193         self.assertEqual(seq_decoded.encode(), seq_encoded)
4194         for expect in expects:
4195             if not expect["presented"]:
4196                 self.assertNotIn(expect["name"], seq_decoded)
4197                 continue
4198             self.assertIn(expect["name"], seq_decoded)
4199             obj = seq_decoded[expect["name"]]
4200             self.assertTrue(obj.decoded)
4201             offset = obj.expl_offset if obj.expled else obj.offset
4202             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4203             self.assertSequenceEqual(
4204                 seq_encoded[offset:offset + tlvlen],
4205                 obj.encode(),
4206             )
4207
4208     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4209     @given(data_strategy())
4210     def test_symmetric_with_seq(self, d):
4211         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
4212         self.assertTrue(seq.ready)
4213         seq_encoded = seq.encode()
4214         seq_decoded, tail = seq.decode(seq_encoded)
4215         self.assertEqual(tail, b"")
4216         self.assertTrue(seq.ready)
4217         self.assertEqual(seq, seq_decoded)
4218         self.assertEqual(seq_decoded.encode(), seq_encoded)
4219         for expect_outer in expect_outers:
4220             if not expect_outer["presented"]:
4221                 self.assertNotIn(expect_outer["name"], seq_decoded)
4222                 continue
4223             self.assertIn(expect_outer["name"], seq_decoded)
4224             obj = seq_decoded[expect_outer["name"]]
4225             self.assertTrue(obj.decoded)
4226             offset = obj.expl_offset if obj.expled else obj.offset
4227             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4228             self.assertSequenceEqual(
4229                 seq_encoded[offset:offset + tlvlen],
4230                 obj.encode(),
4231             )
4232             self._assert_expects(obj, expect_outer["expects"])
4233
4234     @given(data_strategy())
4235     def test_default_disappears(self, d):
4236         _schema = list(d.draw(dictionaries(
4237             text_letters(),
4238             sets(integers(), min_size=2, max_size=2),
4239             min_size=1,
4240         )).items())
4241
4242         class Seq(self.base_klass):
4243             schema = [
4244                 (n, Integer(default=d))
4245                 for n, (_, d) in _schema
4246             ]
4247         seq = Seq()
4248         for name, (value, _) in _schema:
4249             seq[name] = Integer(value)
4250         self.assertEqual(len(seq._value), len(_schema))
4251         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
4252         self.assertGreater(len(seq.encode()), len(empty_seq))
4253         for name, (_, default) in _schema:
4254             seq[name] = Integer(default)
4255         self.assertEqual(len(seq._value), 0)
4256         self.assertSequenceEqual(seq.encode(), empty_seq)
4257
4258     @given(data_strategy())
4259     def test_encoded_default_accepted(self, d):
4260         _schema = list(d.draw(dictionaries(
4261             text_letters(),
4262             integers(),
4263             min_size=1,
4264         )).items())
4265         tags = [tag_encode(tag) for tag in d.draw(sets(
4266             integers(min_value=0),
4267             min_size=len(_schema),
4268             max_size=len(_schema),
4269         ))]
4270
4271         class SeqWithoutDefault(self.base_klass):
4272             schema = [
4273                 (n, Integer(impl=t))
4274                 for (n, _), t in zip(_schema, tags)
4275             ]
4276         seq_without_default = SeqWithoutDefault()
4277         for name, value in _schema:
4278             seq_without_default[name] = Integer(value)
4279         seq_encoded = seq_without_default.encode()
4280
4281         class SeqWithDefault(self.base_klass):
4282             schema = [
4283                 (n, Integer(default=v, impl=t))
4284                 for (n, v), t in zip(_schema, tags)
4285             ]
4286         seq_with_default = SeqWithDefault()
4287         seq_decoded, _ = seq_with_default.decode(seq_encoded)
4288         for name, value in _schema:
4289             self.assertEqual(seq_decoded[name], seq_with_default[name])
4290             self.assertEqual(seq_decoded[name], value)
4291
4292     @given(data_strategy())
4293     def test_missing_from_spec(self, d):
4294         names = list(d.draw(sets(text_letters(), min_size=2)))
4295         tags = [tag_encode(tag) for tag in d.draw(sets(
4296             integers(min_value=0),
4297             min_size=len(names),
4298             max_size=len(names),
4299         ))]
4300         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
4301
4302         class SeqFull(self.base_klass):
4303             schema = [(n, Integer(impl=t)) for n, t in names_tags]
4304         seq_full = SeqFull()
4305         for i, name in enumerate(names):
4306             seq_full[name] = Integer(i)
4307         seq_encoded = seq_full.encode()
4308         altered = names_tags[:-2] + names_tags[-1:]
4309
4310         class SeqMissing(self.base_klass):
4311             schema = [(n, Integer(impl=t)) for n, t in altered]
4312         seq_missing = SeqMissing()
4313         with self.assertRaises(TagMismatch):
4314             seq_missing.decode(seq_encoded)
4315
4316
4317 class TestSequence(SeqMixing, CommonMixin, TestCase):
4318     base_klass = Sequence
4319
4320     @given(
4321         integers(),
4322         binary(min_size=1),
4323     )
4324     def test_remaining(self, value, junk):
4325         class Seq(Sequence):
4326             schema = (
4327                 ("whatever", Integer()),
4328             )
4329         int_encoded = Integer(value).encode()
4330         junked = b"".join((
4331             Sequence.tag_default,
4332             len_encode(len(int_encoded + junk)),
4333             int_encoded + junk,
4334         ))
4335         with assertRaisesRegex(self, DecodeError, "remaining"):
4336             Seq().decode(junked)
4337
4338     @given(sets(text_letters(), min_size=2))
4339     def test_obj_unknown(self, names):
4340         missing = names.pop()
4341
4342         class Seq(Sequence):
4343             schema = [(n, Boolean()) for n in names]
4344         seq = Seq()
4345         with self.assertRaises(ObjUnknown) as err:
4346             seq[missing]
4347         repr(err.exception)
4348         with self.assertRaises(ObjUnknown) as err:
4349             seq[missing] = Boolean()
4350         repr(err.exception)
4351
4352
4353 class TestSet(SeqMixing, CommonMixin, TestCase):
4354     base_klass = Set
4355
4356     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4357     @given(data_strategy())
4358     def test_sorted(self, d):
4359         tags = [
4360             tag_encode(tag) for tag in
4361             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
4362         ]
4363
4364         class Seq(Set):
4365             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
4366         seq = Seq()
4367         for name, _ in Seq.schema:
4368             seq[name] = OctetString(b"")
4369         seq_encoded = seq.encode()
4370         seq_decoded, _ = seq.decode(seq_encoded)
4371         self.assertSequenceEqual(
4372             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
4373             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
4374         )
4375
4376
4377 @composite
4378 def seqof_values_strategy(draw, schema=None, do_expl=False):
4379     if schema is None:
4380         schema = draw(sampled_from((Boolean(), Integer())))
4381     bound_min, bound_max = sorted(draw(sets(
4382         integers(min_value=0, max_value=10),
4383         min_size=2,
4384         max_size=2,
4385     )))
4386     if isinstance(schema, Boolean):
4387         values_generator = booleans().map(Boolean)
4388     elif isinstance(schema, Integer):
4389         values_generator = integers().map(Integer)
4390     values_generator = lists(
4391         values_generator,
4392         min_size=bound_min,
4393         max_size=bound_max,
4394     )
4395     values = draw(one_of(none(), values_generator))
4396     impl = None
4397     expl = None
4398     if do_expl:
4399         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4400     else:
4401         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4402     default = draw(one_of(none(), values_generator))
4403     optional = draw(one_of(none(), booleans()))
4404     _decoded = (
4405         draw(integers(min_value=0)),
4406         draw(integers(min_value=0)),
4407         draw(integers(min_value=0)),
4408     )
4409     return (
4410         schema,
4411         values,
4412         (bound_min, bound_max),
4413         impl,
4414         expl,
4415         default,
4416         optional,
4417         _decoded,
4418     )
4419
4420
4421 class SeqOfMixing(object):
4422     def test_invalid_value_type(self):
4423         with self.assertRaises(InvalidValueType) as err:
4424             self.base_klass(123)
4425         repr(err.exception)
4426
4427     def test_invalid_values_type(self):
4428         class SeqOf(self.base_klass):
4429             schema = Integer()
4430         with self.assertRaises(InvalidValueType) as err:
4431             SeqOf([Integer(123), Boolean(False), Integer(234)])
4432         repr(err.exception)
4433
4434     def test_schema_required(self):
4435         with assertRaisesRegex(self, ValueError, "schema must be specified"):
4436             self.base_klass.__mro__[1]()
4437
4438     @given(booleans(), booleans(), binary(), binary())
4439     def test_comparison(self, value1, value2, tag1, tag2):
4440         class SeqOf(self.base_klass):
4441             schema = Boolean()
4442         obj1 = SeqOf([Boolean(value1)])
4443         obj2 = SeqOf([Boolean(value2)])
4444         self.assertEqual(obj1 == obj2, value1 == value2)
4445         self.assertEqual(obj1 != obj2, value1 != value2)
4446         self.assertEqual(obj1 == list(obj2), value1 == value2)
4447         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
4448         obj1 = SeqOf([Boolean(value1)], impl=tag1)
4449         obj2 = SeqOf([Boolean(value1)], impl=tag2)
4450         self.assertEqual(obj1 == obj2, tag1 == tag2)
4451         self.assertEqual(obj1 != obj2, tag1 != tag2)
4452
4453     @given(lists(booleans()))
4454     def test_iter(self, values):
4455         class SeqOf(self.base_klass):
4456             schema = Boolean()
4457         obj = SeqOf([Boolean(value) for value in values])
4458         self.assertEqual(len(obj), len(values))
4459         for i, value in enumerate(obj):
4460             self.assertEqual(value, values[i])
4461
4462     @given(data_strategy())
4463     def test_ready(self, d):
4464         ready = [Integer(v) for v in d.draw(lists(
4465             integers(),
4466             min_size=1,
4467             max_size=3,
4468         ))]
4469         non_ready = [
4470             Integer() for _ in
4471             range(d.draw(integers(min_value=1, max_value=5)))
4472         ]
4473
4474         class SeqOf(self.base_klass):
4475             schema = Integer()
4476         values = d.draw(permutations(ready + non_ready))
4477         seqof = SeqOf()
4478         for value in values:
4479             seqof.append(value)
4480         self.assertFalse(seqof.ready)
4481         repr(seqof)
4482         pprint(seqof)
4483         with self.assertRaises(ObjNotReady) as err:
4484             seqof.encode()
4485         repr(err.exception)
4486         for i, value in enumerate(values):
4487             self.assertEqual(seqof[i], value)
4488             if not seqof[i].ready:
4489                 seqof[i] = Integer(i)
4490         self.assertTrue(seqof.ready)
4491         repr(seqof)
4492         pprint(seqof)
4493
4494     def test_spec_mismatch(self):
4495         class SeqOf(self.base_klass):
4496             schema = Integer()
4497         seqof = SeqOf()
4498         seqof.append(Integer(123))
4499         with self.assertRaises(ValueError):
4500             seqof.append(Boolean(False))
4501         with self.assertRaises(ValueError):
4502             seqof[0] = Boolean(False)
4503
4504     @given(data_strategy())
4505     def test_bounds_satisfied(self, d):
4506         class SeqOf(self.base_klass):
4507             schema = Boolean()
4508         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
4509         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
4510         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
4511         SeqOf(value=value, bounds=(bound_min, bound_max))
4512
4513     @given(data_strategy())
4514     def test_bounds_unsatisfied(self, d):
4515         class SeqOf(self.base_klass):
4516             schema = Boolean()
4517         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
4518         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
4519         value = [Boolean()] * d.draw(integers(max_value=bound_min - 1))
4520         with self.assertRaises(BoundsError) as err:
4521             SeqOf(value=value, bounds=(bound_min, bound_max))
4522         repr(err.exception)
4523         value = [Boolean()] * d.draw(integers(
4524             min_value=bound_max + 1,
4525             max_value=bound_max + 10,
4526         ))
4527         with self.assertRaises(BoundsError) as err:
4528             SeqOf(value=value, bounds=(bound_min, bound_max))
4529         repr(err.exception)
4530
4531     @given(integers(min_value=1, max_value=10))
4532     def test_out_of_bounds(self, bound_max):
4533         class SeqOf(self.base_klass):
4534             schema = Integer()
4535             bounds = (0, bound_max)
4536         seqof = SeqOf()
4537         for _ in range(bound_max):
4538             seqof.append(Integer(123))
4539         with self.assertRaises(BoundsError):
4540             seqof.append(Integer(123))
4541
4542     @given(data_strategy())
4543     def test_call(self, d):
4544         (
4545             schema_initial,
4546             value_initial,
4547             bounds_initial,
4548             impl_initial,
4549             expl_initial,
4550             default_initial,
4551             optional_initial,
4552             _decoded_initial,
4553         ) = d.draw(seqof_values_strategy())
4554
4555         class SeqOf(self.base_klass):
4556             schema = schema_initial
4557         obj_initial = SeqOf(
4558             value=value_initial,
4559             bounds=bounds_initial,
4560             impl=impl_initial,
4561             expl=expl_initial,
4562             default=default_initial,
4563             optional=optional_initial or False,
4564             _decoded=_decoded_initial,
4565         )
4566         (
4567             _,
4568             value,
4569             bounds,
4570             impl,
4571             expl,
4572             default,
4573             optional,
4574             _decoded,
4575         ) = d.draw(seqof_values_strategy(
4576             schema=schema_initial,
4577             do_expl=impl_initial is None,
4578         ))
4579         if (default is None) and (obj_initial.default is not None):
4580             bounds = None
4581         if (
4582                 (bounds is None) and
4583                 (value is not None) and
4584                 (bounds_initial is not None) and
4585                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
4586         ):
4587             value = None
4588         if (
4589                 (bounds is None) and
4590                 (default is not None) and
4591                 (bounds_initial is not None) and
4592                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
4593         ):
4594             default = None
4595         obj = obj_initial(
4596             value=value,
4597             bounds=bounds,
4598             impl=impl,
4599             expl=expl,
4600             default=default,
4601             optional=optional,
4602         )
4603         if obj.ready:
4604             value_expected = default if value is None else value
4605             value_expected = (
4606                 default_initial if value_expected is None
4607                 else value_expected
4608             )
4609             value_expected = () if value_expected is None else value_expected
4610             self.assertEqual(obj, value_expected)
4611         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4612         self.assertEqual(obj.expl_tag, expl or expl_initial)
4613         self.assertEqual(
4614             obj.default,
4615             default_initial if default is None else default,
4616         )
4617         if obj.default is None:
4618             optional = optional_initial if optional is None else optional
4619             optional = False if optional is None else optional
4620         else:
4621             optional = True
4622         self.assertEqual(obj.optional, optional)
4623         self.assertEqual(
4624             (obj._bound_min, obj._bound_max),
4625             bounds or bounds_initial or (0, float("+inf")),
4626         )
4627
4628     @given(seqof_values_strategy())
4629     def test_copy(self, values):
4630         _schema, value, bounds, impl, expl, default, optional, _decoded = values
4631
4632         class SeqOf(self.base_klass):
4633             schema = _schema
4634         obj = SeqOf(
4635             value=value,
4636             bounds=bounds,
4637             impl=impl,
4638             expl=expl,
4639             default=default,
4640             optional=optional or False,
4641             _decoded=_decoded,
4642         )
4643         obj_copied = obj.copy()
4644         self.assert_copied_basic_fields(obj, obj_copied)
4645         self.assertEqual(obj._bound_min, obj_copied._bound_min)
4646         self.assertEqual(obj._bound_max, obj_copied._bound_max)
4647         self.assertEqual(obj._value, obj_copied._value)
4648
4649     @given(
4650         lists(binary()),
4651         integers(min_value=1).map(tag_encode),
4652     )
4653     def test_stripped(self, values, tag_impl):
4654         class SeqOf(self.base_klass):
4655             schema = OctetString()
4656         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
4657         with self.assertRaises(NotEnoughData):
4658             obj.decode(obj.encode()[:-1])
4659
4660     @given(
4661         lists(binary()),
4662         integers(min_value=1).map(tag_ctxc),
4663     )
4664     def test_stripped_expl(self, values, tag_expl):
4665         class SeqOf(self.base_klass):
4666             schema = OctetString()
4667         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
4668         with self.assertRaises(NotEnoughData):
4669             obj.decode(obj.encode()[:-1])
4670
4671     @given(
4672         integers(min_value=31),
4673         integers(min_value=0),
4674         lists(integers()),
4675     )
4676     def test_bad_tag(self, tag, offset, decode_path):
4677         decode_path = tuple(str(i) for i in decode_path)
4678         with self.assertRaises(DecodeError) as err:
4679             self.base_klass().decode(
4680                 tag_encode(tag)[:-1],
4681                 offset=offset,
4682                 decode_path=decode_path,
4683             )
4684         repr(err.exception)
4685         self.assertEqual(err.exception.offset, offset)
4686         self.assertEqual(err.exception.decode_path, decode_path)
4687
4688     @given(
4689         integers(min_value=128),
4690         integers(min_value=0),
4691         lists(integers()),
4692     )
4693     def test_bad_len(self, l, offset, decode_path):
4694         decode_path = tuple(str(i) for i in decode_path)
4695         with self.assertRaises(DecodeError) as err:
4696             self.base_klass().decode(
4697                 self.base_klass.tag_default + len_encode(l)[:-1],
4698                 offset=offset,
4699                 decode_path=decode_path,
4700             )
4701         repr(err.exception)
4702         self.assertEqual(err.exception.offset, offset)
4703         self.assertEqual(err.exception.decode_path, decode_path)
4704
4705     @given(binary(min_size=1))
4706     def test_tag_mismatch(self, impl):
4707         assume(impl != self.base_klass.tag_default)
4708         with self.assertRaises(TagMismatch):
4709             self.base_klass(impl=impl).decode(self.base_klass().encode())
4710
4711     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4712     @given(
4713         seqof_values_strategy(schema=Integer()),
4714         lists(integers().map(Integer)),
4715         integers(min_value=1).map(tag_ctxc),
4716         integers(min_value=0),
4717         binary(max_size=5),
4718     )
4719     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
4720         _, _, _, _, _, default, optional, _decoded = values
4721
4722         class SeqOf(self.base_klass):
4723             schema = Integer()
4724         obj = SeqOf(
4725             value=value,
4726             default=default,
4727             optional=optional,
4728             _decoded=_decoded,
4729         )
4730         repr(obj)
4731         pprint(obj)
4732         self.assertFalse(obj.expled)
4733         obj_encoded = obj.encode()
4734         obj_expled = obj(value, expl=tag_expl)
4735         self.assertTrue(obj_expled.expled)
4736         repr(obj_expled)
4737         pprint(obj_expled)
4738         obj_expled_encoded = obj_expled.encode()
4739         obj_decoded, tail = obj_expled.decode(
4740             obj_expled_encoded + tail_junk,
4741             offset=offset,
4742         )
4743         repr(obj_decoded)
4744         pprint(obj_decoded)
4745         self.assertEqual(tail, tail_junk)
4746         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
4747         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4748         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4749         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4750         self.assertEqual(
4751             obj_decoded.expl_llen,
4752             len(len_encode(len(obj_encoded))),
4753         )
4754         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4755         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4756         self.assertEqual(
4757             obj_decoded.offset,
4758             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4759         )
4760         self.assertEqual(obj_decoded.expl_offset, offset)
4761         for obj_inner in obj_decoded:
4762             self.assertIn(obj_inner, obj_decoded)
4763             self.assertSequenceEqual(
4764                 obj_inner.encode(),
4765                 obj_expled_encoded[
4766                     obj_inner.offset - offset:
4767                     obj_inner.offset + obj_inner.tlvlen - offset
4768                 ],
4769             )
4770
4771
4772 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
4773     class SeqOf(SequenceOf):
4774         schema = "whatever"
4775     base_klass = SeqOf
4776
4777     def _test_symmetric_compare_objs(self, obj1, obj2):
4778         self.assertEqual(obj1, obj2)
4779         self.assertSequenceEqual(list(obj1), list(obj2))
4780
4781
4782 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
4783     class SeqOf(SetOf):
4784         schema = "whatever"
4785     base_klass = SeqOf
4786
4787     def _test_symmetric_compare_objs(self, obj1, obj2):
4788         self.assertSetEqual(
4789             set(int(v) for v in obj1),
4790             set(int(v) for v in obj2),
4791         )
4792
4793     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4794     @given(data_strategy())
4795     def test_sorted(self, d):
4796         values = [OctetString(v) for v in d.draw(lists(binary()))]
4797
4798         class Seq(SetOf):
4799             schema = OctetString()
4800         seq = Seq(values)
4801         seq_encoded = seq.encode()
4802         seq_decoded, _ = seq.decode(seq_encoded)
4803         self.assertSequenceEqual(
4804             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
4805             b"".join(sorted([v.encode() for v in values])),
4806         )
4807
4808
4809 class TestGoMarshalVectors(TestCase):
4810     def runTest(self):
4811         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
4812         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
4813         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
4814         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
4815         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
4816
4817         class Seq(Sequence):
4818             schema = (
4819                 ("erste", Integer()),
4820                 ("zweite", Integer(optional=True))
4821             )
4822         seq = Seq()
4823         seq["erste"] = Integer(64)
4824         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
4825         seq["erste"] = Integer(0x123456)
4826         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
4827         seq["erste"] = Integer(64)
4828         seq["zweite"] = Integer(65)
4829         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
4830
4831         class NestedSeq(Sequence):
4832             schema = (
4833                 ("nest", Seq()),
4834             )
4835         seq["erste"] = Integer(127)
4836         seq["zweite"] = None
4837         nested = NestedSeq()
4838         nested["nest"] = seq
4839         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
4840
4841         self.assertSequenceEqual(
4842             OctetString(b"\x01\x02\x03").encode(),
4843             hexdec("0403010203"),
4844         )
4845
4846         class Seq(Sequence):
4847             schema = (
4848                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
4849             )
4850         seq = Seq()
4851         seq["erste"] = Integer(64)
4852         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
4853
4854         class Seq(Sequence):
4855             schema = (
4856                 ("erste", Integer(expl=tag_ctxc(5))),
4857             )
4858         seq = Seq()
4859         seq["erste"] = Integer(64)
4860         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
4861
4862         class Seq(Sequence):
4863             schema = (
4864                 ("erste", Null(
4865                     impl=tag_encode(0, klass=TagClassContext),
4866                     optional=True,
4867                 )),
4868             )
4869         seq = Seq()
4870         seq["erste"] = Null()
4871         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
4872         seq["erste"] = None
4873         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
4874
4875         self.assertSequenceEqual(
4876             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
4877             hexdec("170d3730303130313030303030305a"),
4878         )
4879         self.assertSequenceEqual(
4880             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
4881             hexdec("170d3039313131353232353631365a"),
4882         )
4883         self.assertSequenceEqual(
4884             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
4885             hexdec("180f32313030303430353132303130315a"),
4886         )
4887
4888         class Seq(Sequence):
4889             schema = (
4890                 ("erste", GeneralizedTime()),
4891             )
4892         seq = Seq()
4893         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
4894         self.assertSequenceEqual(
4895             seq.encode(),
4896             hexdec("3011180f32303039313131353232353631365a"),
4897         )
4898
4899         self.assertSequenceEqual(
4900             BitString((1, b"\x80")).encode(),
4901             hexdec("03020780"),
4902         )
4903         self.assertSequenceEqual(
4904             BitString((12, b"\x81\xF0")).encode(),
4905             hexdec("03030481f0"),
4906         )
4907
4908         self.assertSequenceEqual(
4909             ObjectIdentifier("1.2.3.4").encode(),
4910             hexdec("06032a0304"),
4911         )
4912         self.assertSequenceEqual(
4913             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
4914             hexdec("06092a864888932d010105"),
4915         )
4916         self.assertSequenceEqual(
4917             ObjectIdentifier("2.100.3").encode(),
4918             hexdec("0603813403"),
4919         )
4920
4921         self.assertSequenceEqual(
4922             PrintableString("test").encode(),
4923             hexdec("130474657374"),
4924         )
4925         self.assertSequenceEqual(
4926             PrintableString("x" * 127).encode(),
4927             hexdec("137F" + "78" * 127),
4928         )
4929         self.assertSequenceEqual(
4930             PrintableString("x" * 128).encode(),
4931             hexdec("138180" + "78" * 128),
4932         )
4933         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
4934
4935         class Seq(Sequence):
4936             schema = (
4937                 ("erste", IA5String()),
4938             )
4939         seq = Seq()
4940         seq["erste"] = IA5String("test")
4941         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
4942
4943         class Seq(Sequence):
4944             schema = (
4945                 ("erste", PrintableString()),
4946             )
4947         seq = Seq()
4948         seq["erste"] = PrintableString("test")
4949         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
4950         seq["erste"] = PrintableString("test*")
4951         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
4952
4953         class Seq(Sequence):
4954             schema = (
4955                 ("erste", Any(optional=True)),
4956                 ("zweite", Integer()),
4957             )
4958         seq = Seq()
4959         seq["zweite"] = Integer(64)
4960         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
4961
4962         class Seq(SetOf):
4963             schema = Integer()
4964         seq = Seq()
4965         seq.append(Integer(10))
4966         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
4967
4968         class _SeqOf(SequenceOf):
4969             schema = PrintableString()
4970
4971         class SeqOf(SequenceOf):
4972             schema = _SeqOf()
4973         _seqof = _SeqOf()
4974         _seqof.append(PrintableString("1"))
4975         seqof = SeqOf()
4976         seqof.append(_seqof)
4977         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
4978
4979         class Seq(Sequence):
4980             schema = (
4981                 ("erste", Integer(default=1)),
4982             )
4983         seq = Seq()
4984         seq["erste"] = Integer(0)
4985         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
4986         seq["erste"] = Integer(1)
4987         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
4988         seq["erste"] = Integer(2)
4989         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
4990
4991
4992 class TestPP(TestCase):
4993     @given(data_strategy())
4994     def test_oid_printing(self, d):
4995         oids = {
4996             str(ObjectIdentifier(k)): v * 2
4997             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
4998         }
4999         chosen = d.draw(sampled_from(sorted(oids)))
5000         chosen_id = oids[chosen]
5001         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
5002         self.assertNotIn(chosen_id, pp_console_row(pp))
5003         self.assertIn(chosen_id, pp_console_row(pp, oids=oids))
5004
5005
5006 class TestAutoAddSlots(TestCase):
5007     def runTest(self):
5008         class Inher(Integer):
5009             pass
5010
5011         with self.assertRaises(AttributeError):
5012             inher = Inher()
5013             inher.unexistent = "whatever"
5014
5015
5016 class TestOIDDefines(TestCase):
5017     @given(data_strategy())
5018     def runTest(self, d):
5019         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
5020         value_name_chosen = d.draw(sampled_from(value_names))
5021         oids = [
5022             ObjectIdentifier(oid)
5023             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
5024         ]
5025         oid_chosen = d.draw(sampled_from(oids))
5026         values = d.draw(lists(
5027             integers(),
5028             min_size=len(value_names),
5029             max_size=len(value_names),
5030         ))
5031         _schema = [
5032             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
5033                 oid: Integer() for oid in oids[:-1]
5034             }),))),
5035         ]
5036         for i, value_name in enumerate(value_names):
5037             _schema.append((value_name, Any(expl=tag_ctxp(i))))
5038
5039         class Seq(Sequence):
5040             schema = _schema
5041         seq = Seq()
5042         for value_name, value in zip(value_names, values):
5043             seq[value_name] = Any(Integer(value).encode())
5044         seq["type"] = oid_chosen
5045         seq, _ = Seq().decode(seq.encode())
5046         for value_name in value_names:
5047             if value_name == value_name_chosen:
5048                 continue
5049             self.assertIsNone(seq[value_name].defined)
5050         if value_name_chosen in oids[:-1]:
5051             self.assertIsNotNone(seq[value_name_chosen].defined)
5052             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
5053             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
5054
5055
5056 class TestDefinesByPath(TestCase):
5057     def test_generated(self):
5058         class Seq(Sequence):
5059             schema = (
5060                 ("type", ObjectIdentifier()),
5061                 ("value", OctetString(expl=tag_ctxc(123))),
5062             )
5063
5064         class SeqInner(Sequence):
5065             schema = (
5066                 ("typeInner", ObjectIdentifier()),
5067                 ("valueInner", Any()),
5068             )
5069
5070         class PairValue(SetOf):
5071             schema = Any()
5072
5073         class Pair(Sequence):
5074             schema = (
5075                 ("type", ObjectIdentifier()),
5076                 ("value", PairValue()),
5077             )
5078
5079         class Pairs(SequenceOf):
5080             schema = Pair()
5081
5082         (
5083             type_integered,
5084             type_sequenced,
5085             type_innered,
5086             type_octet_stringed,
5087         ) = [
5088             ObjectIdentifier(oid)
5089             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
5090         ]
5091         seq_integered = Seq()
5092         seq_integered["type"] = type_integered
5093         seq_integered["value"] = OctetString(Integer(123).encode())
5094         seq_integered_raw = seq_integered.encode()
5095
5096         pairs = Pairs()
5097         pairs_input = (
5098             (type_octet_stringed, OctetString(b"whatever")),
5099             (type_integered, Integer(123)),
5100             (type_octet_stringed, OctetString(b"whenever")),
5101             (type_integered, Integer(234)),
5102         )
5103         for t, v in pairs_input:
5104             pair = Pair()
5105             pair["type"] = t
5106             pair["value"] = PairValue((Any(v),))
5107             pairs.append(pair)
5108         seq_inner = SeqInner()
5109         seq_inner["typeInner"] = type_innered
5110         seq_inner["valueInner"] = Any(pairs)
5111         seq_sequenced = Seq()
5112         seq_sequenced["type"] = type_sequenced
5113         seq_sequenced["value"] = OctetString(seq_inner.encode())
5114         seq_sequenced_raw = seq_sequenced.encode()
5115
5116         defines_by_path = []
5117         seq_integered, _ = Seq().decode(seq_integered_raw)
5118         self.assertIsNone(seq_integered["value"].defined)
5119         defines_by_path.append(
5120             (("type",), ((("value",), {
5121                 type_integered: Integer(),
5122                 type_sequenced: SeqInner(),
5123             }),))
5124         )
5125         seq_integered, _ = Seq().decode(
5126             seq_integered_raw,
5127             ctx={"defines_by_path": defines_by_path},
5128         )
5129         self.assertIsNotNone(seq_integered["value"].defined)
5130         self.assertEqual(seq_integered["value"].defined[0], type_integered)
5131         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
5132         self.assertTrue(seq_integered_raw[
5133             seq_integered["value"].defined[1].offset:
5134         ].startswith(Integer(123).encode()))
5135
5136         seq_sequenced, _ = Seq().decode(
5137             seq_sequenced_raw,
5138             ctx={"defines_by_path": defines_by_path},
5139         )
5140         self.assertIsNotNone(seq_sequenced["value"].defined)
5141         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5142         seq_inner = seq_sequenced["value"].defined[1]
5143         self.assertIsNone(seq_inner["valueInner"].defined)
5144
5145         defines_by_path.append((
5146             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
5147             ((("valueInner",), {type_innered: Pairs()}),),
5148         ))
5149         seq_sequenced, _ = Seq().decode(
5150             seq_sequenced_raw,
5151             ctx={"defines_by_path": defines_by_path},
5152         )
5153         self.assertIsNotNone(seq_sequenced["value"].defined)
5154         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5155         seq_inner = seq_sequenced["value"].defined[1]
5156         self.assertIsNotNone(seq_inner["valueInner"].defined)
5157         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5158         pairs = seq_inner["valueInner"].defined[1]
5159         for pair in pairs:
5160             self.assertIsNone(pair["value"][0].defined)
5161
5162         defines_by_path.append((
5163             (
5164                 "value",
5165                 DecodePathDefBy(type_sequenced),
5166                 "valueInner",
5167                 DecodePathDefBy(type_innered),
5168                 any,
5169                 "type",
5170             ),
5171             ((("value",), {
5172                 type_integered: Integer(),
5173                 type_octet_stringed: OctetString(),
5174             }),),
5175         ))
5176         seq_sequenced, _ = Seq().decode(
5177             seq_sequenced_raw,
5178             ctx={"defines_by_path": defines_by_path},
5179         )
5180         self.assertIsNotNone(seq_sequenced["value"].defined)
5181         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5182         seq_inner = seq_sequenced["value"].defined[1]
5183         self.assertIsNotNone(seq_inner["valueInner"].defined)
5184         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5185         pairs_got = seq_inner["valueInner"].defined[1]
5186         for pair_input, pair_got in zip(pairs_input, pairs_got):
5187             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
5188             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
5189
5190     @given(oid_strategy(), integers())
5191     def test_simple(self, oid, tgt):
5192         class Inner(Sequence):
5193             schema = (
5194                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
5195                     ObjectIdentifier(oid): Integer(),
5196                 }),))),
5197             )
5198
5199         class Outer(Sequence):
5200             schema = (
5201                 ("inner", Inner()),
5202                 ("tgt", OctetString()),
5203             )
5204
5205         inner = Inner()
5206         inner["oid"] = ObjectIdentifier(oid)
5207         outer = Outer()
5208         outer["inner"] = inner
5209         outer["tgt"] = OctetString(Integer(tgt).encode())
5210         decoded, _ = Outer().decode(outer.encode())
5211         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
5212
5213
5214 class TestAbsDecodePath(TestCase):
5215     @given(
5216         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5217         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5218     )
5219     def test_concat(self, decode_path, rel_path):
5220         self.assertSequenceEqual(
5221             abs_decode_path(decode_path, rel_path),
5222             decode_path + rel_path,
5223         )
5224
5225     @given(
5226         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5227         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5228     )
5229     def test_abs(self, decode_path, rel_path):
5230         self.assertSequenceEqual(
5231             abs_decode_path(decode_path, ("/",) + rel_path),
5232             rel_path,
5233         )
5234
5235     @given(
5236         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
5237         integers(min_value=1, max_value=3),
5238         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5239     )
5240     def test_dots(self, decode_path, number_of_dots, rel_path):
5241         self.assertSequenceEqual(
5242             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
5243             decode_path[:-number_of_dots] + rel_path,
5244         )
5245
5246
5247 class TestStrictDefaultExistence(TestCase):
5248     @given(data_strategy())
5249     def runTest(self, d):
5250         count = d.draw(integers(min_value=1, max_value=10))
5251         chosen = d.draw(integers(min_value=0, max_value=count - 1))
5252         _schema = [
5253             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
5254             for i in range(count)
5255         ]
5256
5257         class Seq(Sequence):
5258             schema = _schema
5259         seq = Seq()
5260         for i in range(count):
5261             seq["int%d" % i] = Integer(123)
5262         raw = seq.encode()
5263         chosen = "int%d" % chosen
5264         seq.specs[chosen] = seq.specs[chosen](default=123)
5265         seq.decode(raw)
5266         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5267             seq.decode(raw, ctx={"strict_default_existence": True})