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