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