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