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