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