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