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