]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Fix CHOICE's tlvlen with explicitly tagged value
[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
3613 class TestUTCTime(TimeMixin, CommonMixin, TestCase):
3614     base_klass = UTCTime
3615     omit_ms = True
3616     min_datetime = datetime(2000, 1, 1)
3617     max_datetime = datetime(2049, 12, 31)
3618
3619     def test_go_vectors_invalid(self):
3620         for data in ((
3621                 b"a10506234540Z",
3622                 b"91a506234540Z",
3623                 b"9105a6234540Z",
3624                 b"910506a34540Z",
3625                 b"910506334a40Z",
3626                 b"91050633444aZ",
3627                 b"910506334461Z",
3628                 b"910506334400Za",
3629                 b"000100000000Z",
3630                 b"101302030405Z",
3631                 b"100002030405Z",
3632                 b"100100030405Z",
3633                 b"100132030405Z",
3634                 b"100231030405Z",
3635                 b"100102240405Z",
3636                 b"100102036005Z",
3637                 b"100102030460Z",
3638                 b"-100102030410Z",
3639                 b"10-0102030410Z",
3640                 b"10-0002030410Z",
3641                 b"1001-02030410Z",
3642                 b"100102-030410Z",
3643                 b"10010203-0410Z",
3644                 b"1001020304-10Z",
3645                 # These ones are INVALID in *DER*, but accepted
3646                 # by Go's encoding/asn1
3647                 b"910506164540-0700",
3648                 b"910506164540+0730",
3649                 b"9105062345Z",
3650                 b"5105062345Z",
3651         )):
3652             with self.assertRaises(DecodeError) as err:
3653                 UTCTime(data)
3654             repr(err.exception)
3655
3656     def test_go_vectors_valid(self):
3657         self.assertEqual(
3658             UTCTime(b"910506234540Z").todatetime(),
3659             datetime(1991, 5, 6, 23, 45, 40, 0),
3660         )
3661
3662     @given(integers(min_value=0, max_value=49))
3663     def test_pre50(self, year):
3664         self.assertEqual(
3665             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3666             2000 + year,
3667         )
3668
3669     @given(integers(min_value=50, max_value=99))
3670     def test_post50(self, year):
3671         self.assertEqual(
3672             UTCTime(("%02d1231235959Z" % year).encode("ascii")).todatetime().year,
3673             1900 + year,
3674         )
3675
3676
3677 @composite
3678 def any_values_strategy(draw, do_expl=False):
3679     value = draw(one_of(none(), binary()))
3680     expl = None
3681     if do_expl:
3682         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3683     optional = draw(one_of(none(), booleans()))
3684     _decoded = (
3685         draw(integers(min_value=0)),
3686         draw(integers(min_value=0)),
3687         draw(integers(min_value=0)),
3688     )
3689     return (value, expl, optional, _decoded)
3690
3691
3692 class AnyInherited(Any):
3693     pass
3694
3695
3696 class TestAny(CommonMixin, TestCase):
3697     base_klass = Any
3698
3699     def test_invalid_value_type(self):
3700         with self.assertRaises(InvalidValueType) as err:
3701             Any(123)
3702         repr(err.exception)
3703
3704     @given(booleans())
3705     def test_optional(self, optional):
3706         obj = Any(optional=optional)
3707         self.assertEqual(obj.optional, optional)
3708
3709     @given(binary())
3710     def test_ready(self, value):
3711         obj = Any()
3712         self.assertFalse(obj.ready)
3713         repr(obj)
3714         pprint(obj)
3715         with self.assertRaises(ObjNotReady) as err:
3716             obj.encode()
3717         repr(err.exception)
3718         obj = Any(value)
3719         self.assertTrue(obj.ready)
3720         repr(obj)
3721         pprint(obj)
3722
3723     @given(integers())
3724     def test_basic(self, value):
3725         integer_encoded = Integer(value).encode()
3726         for obj in (
3727                 Any(integer_encoded),
3728                 Any(Integer(value)),
3729                 Any(Any(Integer(value))),
3730         ):
3731             self.assertSequenceEqual(bytes(obj), integer_encoded)
3732             self.assertEqual(
3733                 obj.decode(obj.encode())[0].vlen,
3734                 len(integer_encoded),
3735             )
3736             repr(obj)
3737             pprint(obj)
3738             self.assertSequenceEqual(obj.encode(), integer_encoded)
3739
3740     @given(binary(), binary())
3741     def test_comparison(self, value1, value2):
3742         for klass in (Any, AnyInherited):
3743             obj1 = klass(value1)
3744             obj2 = klass(value2)
3745             self.assertEqual(obj1 == obj2, value1 == value2)
3746             self.assertEqual(obj1 != obj2, value1 != value2)
3747             self.assertEqual(obj1 == bytes(obj2), value1 == value2)
3748
3749     @given(data_strategy())
3750     def test_call(self, d):
3751         for klass in (Any, AnyInherited):
3752             (
3753                 value_initial,
3754                 expl_initial,
3755                 optional_initial,
3756                 _decoded_initial,
3757             ) = d.draw(any_values_strategy())
3758             obj_initial = klass(
3759                 value_initial,
3760                 expl_initial,
3761                 optional_initial or False,
3762                 _decoded_initial,
3763             )
3764             (
3765                 value,
3766                 expl,
3767                 optional,
3768                 _decoded,
3769             ) = d.draw(any_values_strategy(do_expl=True))
3770             obj = obj_initial(value, expl, optional)
3771             if obj.ready:
3772                 value_expected = None if value is None else value
3773                 self.assertEqual(obj, value_expected)
3774             self.assertEqual(obj.expl_tag, expl or expl_initial)
3775             if obj.default is None:
3776                 optional = optional_initial if optional is None else optional
3777                 optional = False if optional is None else optional
3778             self.assertEqual(obj.optional, optional)
3779
3780     def test_simultaneous_impl_expl(self):
3781         # override it, as Any does not have implicit tag
3782         pass
3783
3784     def test_decoded(self):
3785         # override it, as Any does not have implicit tag
3786         pass
3787
3788     @given(any_values_strategy())
3789     def test_copy(self, values):
3790         for klass in (Any, AnyInherited):
3791             obj = klass(*values)
3792             obj_copied = obj.copy()
3793             self.assert_copied_basic_fields(obj, obj_copied)
3794             self.assertEqual(obj._value, obj_copied._value)
3795
3796     @given(binary().map(OctetString))
3797     def test_stripped(self, value):
3798         obj = Any(value)
3799         with self.assertRaises(NotEnoughData):
3800             obj.decode(obj.encode()[:-1])
3801
3802     @given(
3803         binary(),
3804         integers(min_value=1).map(tag_ctxc),
3805     )
3806     def test_stripped_expl(self, value, tag_expl):
3807         obj = Any(value, expl=tag_expl)
3808         with self.assertRaises(NotEnoughData):
3809             obj.decode(obj.encode()[:-1])
3810
3811     @given(
3812         integers(min_value=31),
3813         integers(min_value=0),
3814         decode_path_strat,
3815     )
3816     def test_bad_tag(self, tag, offset, decode_path):
3817         with self.assertRaises(DecodeError) as err:
3818             Any().decode(
3819                 tag_encode(tag)[:-1],
3820                 offset=offset,
3821                 decode_path=decode_path,
3822             )
3823         repr(err.exception)
3824         self.assertEqual(err.exception.offset, offset)
3825         self.assertEqual(err.exception.decode_path, decode_path)
3826
3827     @given(
3828         integers(min_value=128),
3829         integers(min_value=0),
3830         decode_path_strat,
3831     )
3832     def test_bad_len(self, l, offset, decode_path):
3833         with self.assertRaises(DecodeError) as err:
3834             Any().decode(
3835                 Any.tag_default + len_encode(l)[:-1],
3836                 offset=offset,
3837                 decode_path=decode_path,
3838             )
3839         repr(err.exception)
3840         self.assertEqual(err.exception.offset, offset)
3841         self.assertEqual(err.exception.decode_path, decode_path)
3842
3843     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
3844     @given(
3845         any_values_strategy(),
3846         integers().map(lambda x: Integer(x).encode()),
3847         integers(min_value=1).map(tag_ctxc),
3848         integers(min_value=0),
3849         binary(max_size=5),
3850     )
3851     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
3852         for klass in (Any, AnyInherited):
3853             _, _, optional, _decoded = values
3854             obj = klass(value=value, optional=optional, _decoded=_decoded)
3855             repr(obj)
3856             pprint(obj)
3857             self.assertFalse(obj.expled)
3858             obj_encoded = obj.encode()
3859             obj_expled = obj(value, expl=tag_expl)
3860             self.assertTrue(obj_expled.expled)
3861             repr(obj_expled)
3862             pprint(obj_expled)
3863             obj_expled_encoded = obj_expled.encode()
3864             obj_decoded, tail = obj_expled.decode(
3865                 obj_expled_encoded + tail_junk,
3866                 offset=offset,
3867             )
3868             repr(obj_decoded)
3869             pprint(obj_decoded)
3870             self.assertEqual(tail, tail_junk)
3871             self.assertEqual(obj_decoded, obj_expled)
3872             self.assertEqual(bytes(obj_decoded), bytes(obj_expled))
3873             self.assertEqual(bytes(obj_decoded), bytes(obj))
3874             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
3875             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
3876             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
3877             self.assertEqual(
3878                 obj_decoded.expl_llen,
3879                 len(len_encode(len(obj_encoded))),
3880             )
3881             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
3882             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
3883             self.assertEqual(
3884                 obj_decoded.offset,
3885                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
3886             )
3887             self.assertEqual(obj_decoded.expl_offset, offset)
3888             self.assertEqual(obj_decoded.tlen, 0)
3889             self.assertEqual(obj_decoded.llen, 0)
3890             self.assertEqual(obj_decoded.vlen, len(value))
3891
3892     @given(
3893         integers(min_value=1).map(tag_ctxc),
3894         integers(min_value=0, max_value=3),
3895         integers(min_value=0),
3896         decode_path_strat,
3897         binary(),
3898     )
3899     def test_indefinite(self, expl, chunks, offset, decode_path, junk):
3900         chunk = Boolean(False, expl=expl).encode()
3901         encoded = (
3902             OctetString.tag_default +
3903             LENINDEF +
3904             b"".join([chunk] * chunks) +
3905             EOC
3906         )
3907         obj, tail = Any().decode(
3908             encoded + junk,
3909             offset=offset,
3910             decode_path=decode_path,
3911             ctx={"bered": True},
3912         )
3913         self.assertSequenceEqual(tail, junk)
3914         self.assertEqual(obj.offset, offset)
3915         self.assertEqual(obj.tlvlen, len(encoded))
3916         with self.assertRaises(NotEnoughData) as err:
3917             Any().decode(
3918                 encoded[:-1],
3919                 offset=offset,
3920                 decode_path=decode_path,
3921                 ctx={"bered": True},
3922             )
3923         self.assertEqual(err.exception.offset, offset + 1 + 1 + len(chunk) * chunks)
3924         self.assertEqual(err.exception.decode_path, decode_path + (str(chunks),))
3925
3926
3927 @composite
3928 def choice_values_strategy(draw, value_required=False, schema=None, do_expl=False):
3929     if schema is None:
3930         names = list(draw(sets(text_letters(), min_size=1, max_size=5)))
3931         tags = [{tag_type: tag_value} for tag_type, tag_value in draw(sets(
3932             one_of(
3933                 tuples(just("impl"), integers(min_value=0).map(tag_encode)),
3934                 tuples(just("expl"), integers(min_value=0).map(tag_ctxp)),
3935             ),
3936             min_size=len(names),
3937             max_size=len(names),
3938         ))]
3939         schema = [
3940             (name, Integer(**tag_kwargs))
3941             for name, tag_kwargs in zip(names, tags)
3942         ]
3943     value = None
3944     if value_required or draw(booleans()):
3945         value = draw(tuples(
3946             sampled_from([name for name, _ in schema]),
3947             integers().map(Integer),
3948         ))
3949     expl = None
3950     if do_expl:
3951         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3952     default = draw(one_of(
3953         none(),
3954         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
3955     ))
3956     optional = draw(one_of(none(), booleans()))
3957     _decoded = (
3958         draw(integers(min_value=0)),
3959         draw(integers(min_value=0)),
3960         draw(integers(min_value=0)),
3961     )
3962     return (schema, value, expl, default, optional, _decoded)
3963
3964
3965 class ChoiceInherited(Choice):
3966     pass
3967
3968
3969 class TestChoice(CommonMixin, TestCase):
3970     class Wahl(Choice):
3971         schema = (("whatever", Boolean()),)
3972     base_klass = Wahl
3973
3974     def test_schema_required(self):
3975         with assertRaisesRegex(self, ValueError, "schema must be specified"):
3976             Choice()
3977
3978     def test_impl_forbidden(self):
3979         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
3980             Choice(impl=b"whatever")
3981
3982     def test_invalid_value_type(self):
3983         with self.assertRaises(InvalidValueType) as err:
3984             self.base_klass(123)
3985         repr(err.exception)
3986         with self.assertRaises(ObjUnknown) as err:
3987             self.base_klass(("whenever", Boolean(False)))
3988         repr(err.exception)
3989         with self.assertRaises(InvalidValueType) as err:
3990             self.base_klass(("whatever", Integer(123)))
3991         repr(err.exception)
3992
3993     @given(booleans())
3994     def test_optional(self, optional):
3995         obj = self.base_klass(
3996             default=self.base_klass(("whatever", Boolean(False))),
3997             optional=optional,
3998         )
3999         self.assertTrue(obj.optional)
4000
4001     @given(booleans())
4002     def test_ready(self, value):
4003         obj = self.base_klass()
4004         self.assertFalse(obj.ready)
4005         repr(obj)
4006         pprint(obj)
4007         self.assertIsNone(obj["whatever"])
4008         with self.assertRaises(ObjNotReady) as err:
4009             obj.encode()
4010         repr(err.exception)
4011         obj["whatever"] = Boolean()
4012         self.assertFalse(obj.ready)
4013         repr(obj)
4014         pprint(obj)
4015         obj["whatever"] = Boolean(value)
4016         self.assertTrue(obj.ready)
4017         repr(obj)
4018         pprint(obj)
4019
4020     @given(booleans(), booleans())
4021     def test_comparison(self, value1, value2):
4022         class WahlInherited(self.base_klass):
4023             pass
4024         for klass in (self.base_klass, WahlInherited):
4025             obj1 = klass(("whatever", Boolean(value1)))
4026             obj2 = klass(("whatever", Boolean(value2)))
4027             self.assertEqual(obj1 == obj2, value1 == value2)
4028             self.assertEqual(obj1 != obj2, value1 != value2)
4029             self.assertEqual(obj1 == obj2._value, value1 == value2)
4030             self.assertFalse(obj1 == obj2._value[1])
4031
4032     @given(data_strategy())
4033     def test_call(self, d):
4034         for klass in (Choice, ChoiceInherited):
4035             (
4036                 schema_initial,
4037                 value_initial,
4038                 expl_initial,
4039                 default_initial,
4040                 optional_initial,
4041                 _decoded_initial,
4042             ) = d.draw(choice_values_strategy())
4043
4044             class Wahl(klass):
4045                 schema = schema_initial
4046             obj_initial = Wahl(
4047                 value=value_initial,
4048                 expl=expl_initial,
4049                 default=default_initial,
4050                 optional=optional_initial or False,
4051                 _decoded=_decoded_initial,
4052             )
4053             (
4054                 _,
4055                 value,
4056                 expl,
4057                 default,
4058                 optional,
4059                 _decoded,
4060             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
4061             obj = obj_initial(value, expl, default, optional)
4062             if obj.ready:
4063                 value_expected = default if value is None else value
4064                 value_expected = (
4065                     default_initial if value_expected is None
4066                     else value_expected
4067                 )
4068                 self.assertEqual(obj.choice, value_expected[0])
4069                 self.assertEqual(obj.value, int(value_expected[1]))
4070             self.assertEqual(obj.expl_tag, expl or expl_initial)
4071             default_expect = default_initial if default is None else default
4072             if default_expect is not None:
4073                 self.assertEqual(obj.default.choice, default_expect[0])
4074                 self.assertEqual(obj.default.value, int(default_expect[1]))
4075             if obj.default is None:
4076                 optional = optional_initial if optional is None else optional
4077                 optional = False if optional is None else optional
4078             else:
4079                 optional = True
4080             self.assertEqual(obj.optional, optional)
4081             self.assertEqual(obj.specs, obj_initial.specs)
4082
4083     def test_simultaneous_impl_expl(self):
4084         # override it, as Any does not have implicit tag
4085         pass
4086
4087     def test_decoded(self):
4088         # override it, as Any does not have implicit tag
4089         pass
4090
4091     @given(choice_values_strategy())
4092     def test_copy(self, values):
4093         _schema, value, expl, default, optional, _decoded = values
4094
4095         class Wahl(self.base_klass):
4096             schema = _schema
4097         obj = Wahl(
4098             value=value,
4099             expl=expl,
4100             default=default,
4101             optional=optional or False,
4102             _decoded=_decoded,
4103         )
4104         obj_copied = obj.copy()
4105         self.assertIsNone(obj.tag)
4106         self.assertIsNone(obj_copied.tag)
4107         # hack for assert_copied_basic_fields
4108         obj.tag = "whatever"
4109         obj_copied.tag = "whatever"
4110         self.assert_copied_basic_fields(obj, obj_copied)
4111         self.assertEqual(obj._value, obj_copied._value)
4112         self.assertEqual(obj.specs, obj_copied.specs)
4113
4114     @given(booleans())
4115     def test_stripped(self, value):
4116         obj = self.base_klass(("whatever", Boolean(value)))
4117         with self.assertRaises(NotEnoughData):
4118             obj.decode(obj.encode()[:-1])
4119
4120     @given(
4121         booleans(),
4122         integers(min_value=1).map(tag_ctxc),
4123     )
4124     def test_stripped_expl(self, value, tag_expl):
4125         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
4126         with self.assertRaises(NotEnoughData):
4127             obj.decode(obj.encode()[:-1])
4128
4129     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4130     @given(data_strategy())
4131     def test_symmetric(self, d):
4132         _schema, value, _, default, optional, _decoded = d.draw(
4133             choice_values_strategy(value_required=True)
4134         )
4135         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4136         offset = d.draw(integers(min_value=0))
4137         tail_junk = d.draw(binary(max_size=5))
4138
4139         class Wahl(self.base_klass):
4140             schema = _schema
4141         obj = Wahl(
4142             value=value,
4143             default=default,
4144             optional=optional,
4145             _decoded=_decoded,
4146         )
4147         repr(obj)
4148         pprint(obj)
4149         self.assertFalse(obj.expled)
4150         obj_encoded = obj.encode()
4151         obj_expled = obj(value, expl=tag_expl)
4152         self.assertTrue(obj_expled.expled)
4153         repr(obj_expled)
4154         pprint(obj_expled)
4155         obj_expled_encoded = obj_expled.encode()
4156         obj_decoded, tail = obj_expled.decode(
4157             obj_expled_encoded + tail_junk,
4158             offset=offset,
4159         )
4160         repr(obj_decoded)
4161         pprint(obj_decoded)
4162         self.assertEqual(tail, tail_junk)
4163         self.assertEqual(obj_decoded, obj_expled)
4164         self.assertEqual(obj_decoded.choice, obj_expled.choice)
4165         self.assertEqual(obj_decoded.value, obj_expled.value)
4166         self.assertEqual(obj_decoded.choice, obj.choice)
4167         self.assertEqual(obj_decoded.value, obj.value)
4168         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4169         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4170         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4171         self.assertEqual(
4172             obj_decoded.expl_llen,
4173             len(len_encode(len(obj_encoded))),
4174         )
4175         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4176         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4177         self.assertEqual(
4178             obj_decoded.offset,
4179             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4180         )
4181         self.assertEqual(obj_decoded.expl_offset, offset)
4182         self.assertSequenceEqual(
4183             obj_expled_encoded[
4184                 obj_decoded.value.fulloffset - offset:
4185                 obj_decoded.value.fulloffset + obj_decoded.value.fulllen - offset
4186             ],
4187             obj_encoded,
4188         )
4189
4190     @given(integers())
4191     def test_set_get(self, value):
4192         class Wahl(Choice):
4193             schema = (
4194                 ("erste", Boolean()),
4195                 ("zweite", Integer()),
4196             )
4197         obj = Wahl()
4198         with self.assertRaises(ObjUnknown) as err:
4199             obj["whatever"] = "whenever"
4200         with self.assertRaises(InvalidValueType) as err:
4201             obj["zweite"] = Boolean(False)
4202         obj["zweite"] = Integer(value)
4203         repr(err.exception)
4204         with self.assertRaises(ObjUnknown) as err:
4205             obj["whatever"]
4206         repr(err.exception)
4207         self.assertIsNone(obj["erste"])
4208         self.assertEqual(obj["zweite"], Integer(value))
4209
4210     def test_tag_mismatch(self):
4211         class Wahl(Choice):
4212             schema = (
4213                 ("erste", Boolean()),
4214             )
4215         int_encoded = Integer(123).encode()
4216         bool_encoded = Boolean(False).encode()
4217         obj = Wahl()
4218         obj.decode(bool_encoded)
4219         with self.assertRaises(TagMismatch):
4220             obj.decode(int_encoded)
4221
4222     def test_tag_mismatch_underlying(self):
4223         class SeqOfBoolean(SequenceOf):
4224             schema = Boolean()
4225
4226         class SeqOfInteger(SequenceOf):
4227             schema = Integer()
4228
4229         class Wahl(Choice):
4230             schema = (
4231                 ("erste", SeqOfBoolean()),
4232             )
4233
4234         int_encoded = SeqOfInteger((Integer(123),)).encode()
4235         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
4236         obj = Wahl()
4237         obj.decode(bool_encoded)
4238         with self.assertRaises(TagMismatch) as err:
4239             obj.decode(int_encoded)
4240         self.assertEqual(err.exception.decode_path, ("erste", "0"))
4241
4242
4243 @composite
4244 def seq_values_strategy(draw, seq_klass, do_expl=False):
4245     value = None
4246     if draw(booleans()):
4247         value = seq_klass()
4248         value._value = {
4249             k: v for k, v in draw(dictionaries(
4250                 integers(),
4251                 one_of(
4252                     booleans().map(Boolean),
4253                     integers().map(Integer),
4254                 ),
4255             )).items()
4256         }
4257     schema = None
4258     if draw(booleans()):
4259         schema = list(draw(dictionaries(
4260             integers(),
4261             one_of(
4262                 booleans().map(Boolean),
4263                 integers().map(Integer),
4264             ),
4265         )).items())
4266     impl = None
4267     expl = None
4268     if do_expl:
4269         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4270     else:
4271         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4272     default = None
4273     if draw(booleans()):
4274         default = seq_klass()
4275         default._value = {
4276             k: v for k, v in draw(dictionaries(
4277                 integers(),
4278                 one_of(
4279                     booleans().map(Boolean),
4280                     integers().map(Integer),
4281                 ),
4282             )).items()
4283         }
4284     optional = draw(one_of(none(), booleans()))
4285     _decoded = (
4286         draw(integers(min_value=0)),
4287         draw(integers(min_value=0)),
4288         draw(integers(min_value=0)),
4289     )
4290     return (value, schema, impl, expl, default, optional, _decoded)
4291
4292
4293 @composite
4294 def sequence_strategy(draw, seq_klass):
4295     inputs = draw(lists(
4296         one_of(
4297             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
4298             tuples(just(Integer), integers(), one_of(none(), integers())),
4299         ),
4300         max_size=6,
4301     ))
4302     tags = draw(sets(
4303         integers(min_value=1),
4304         min_size=len(inputs),
4305         max_size=len(inputs),
4306     ))
4307     inits = [
4308         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4309         for tag, expled in zip(tags, draw(lists(
4310             booleans(),
4311             min_size=len(inputs),
4312             max_size=len(inputs),
4313         )))
4314     ]
4315     empties = []
4316     for i, optional in enumerate(draw(lists(
4317             sampled_from(("required", "optional", "empty")),
4318             min_size=len(inputs),
4319             max_size=len(inputs),
4320     ))):
4321         if optional in ("optional", "empty"):
4322             inits[i]["optional"] = True
4323         if optional == "empty":
4324             empties.append(i)
4325     empties = set(empties)
4326     names = list(draw(sets(
4327         text_printable,
4328         min_size=len(inputs),
4329         max_size=len(inputs),
4330     )))
4331     schema = []
4332     for i, (klass, value, default) in enumerate(inputs):
4333         schema.append((names[i], klass(default=default, **inits[i])))
4334     seq_name = draw(text_letters())
4335     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4336     seq = Seq()
4337     expects = []
4338     for i, (klass, value, default) in enumerate(inputs):
4339         name = names[i]
4340         _, spec = schema[i]
4341         expect = {
4342             "name": name,
4343             "optional": False,
4344             "presented": False,
4345             "default_value": None if spec.default is None else default,
4346             "value": None,
4347         }
4348         if i in empties:
4349             expect["optional"] = True
4350         else:
4351             expect["presented"] = True
4352             expect["value"] = value
4353             if spec.optional:
4354                 expect["optional"] = True
4355             if default is not None and default == value:
4356                 expect["presented"] = False
4357             seq[name] = klass(value)
4358         expects.append(expect)
4359     return seq, expects
4360
4361
4362 @composite
4363 def sequences_strategy(draw, seq_klass):
4364     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
4365     inits = [
4366         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4367         for tag, expled in zip(tags, draw(lists(
4368             booleans(),
4369             min_size=len(tags),
4370             max_size=len(tags),
4371         )))
4372     ]
4373     defaulted = set(
4374         i for i, is_default in enumerate(draw(lists(
4375             booleans(),
4376             min_size=len(tags),
4377             max_size=len(tags),
4378         ))) if is_default
4379     )
4380     names = list(draw(sets(
4381         text_printable,
4382         min_size=len(tags),
4383         max_size=len(tags),
4384     )))
4385     seq_expectses = draw(lists(
4386         sequence_strategy(seq_klass=seq_klass),
4387         min_size=len(tags),
4388         max_size=len(tags),
4389     ))
4390     seqs = [seq for seq, _ in seq_expectses]
4391     schema = []
4392     for i, (name, seq) in enumerate(zip(names, seqs)):
4393         schema.append((
4394             name,
4395             seq(default=(seq if i in defaulted else None), **inits[i]),
4396         ))
4397     seq_name = draw(text_letters())
4398     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4399     seq_outer = Seq()
4400     expect_outers = []
4401     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
4402         expect = {
4403             "name": name,
4404             "expects": expects_inner,
4405             "presented": False,
4406         }
4407         seq_outer[name] = seq_inner
4408         if seq_outer.specs[name].default is None:
4409             expect["presented"] = True
4410         expect_outers.append(expect)
4411     return seq_outer, expect_outers
4412
4413
4414 class SeqMixing(object):
4415     def test_invalid_value_type(self):
4416         with self.assertRaises(InvalidValueType) as err:
4417             self.base_klass(123)
4418         repr(err.exception)
4419
4420     def test_invalid_value_type_set(self):
4421         class Seq(self.base_klass):
4422             schema = (("whatever", Boolean()),)
4423         seq = Seq()
4424         with self.assertRaises(InvalidValueType) as err:
4425             seq["whatever"] = Integer(123)
4426         repr(err.exception)
4427
4428     @given(booleans())
4429     def test_optional(self, optional):
4430         obj = self.base_klass(default=self.base_klass(), optional=optional)
4431         self.assertTrue(obj.optional)
4432
4433     @given(data_strategy())
4434     def test_ready(self, d):
4435         ready = {
4436             str(i): v for i, v in enumerate(d.draw(lists(
4437                 booleans(),
4438                 min_size=1,
4439                 max_size=3,
4440             )))
4441         }
4442         non_ready = {
4443             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
4444                 booleans(),
4445                 min_size=1,
4446                 max_size=3,
4447             )))
4448         }
4449         schema_input = []
4450         for name in d.draw(permutations(
4451                 list(ready.keys()) + list(non_ready.keys()),
4452         )):
4453             schema_input.append((name, Boolean()))
4454
4455         class Seq(self.base_klass):
4456             schema = tuple(schema_input)
4457         seq = Seq()
4458         for name in ready.keys():
4459             seq[name]
4460             seq[name] = Boolean()
4461         self.assertFalse(seq.ready)
4462         repr(seq)
4463         pprint(seq)
4464         for name, value in ready.items():
4465             seq[name] = Boolean(value)
4466         self.assertFalse(seq.ready)
4467         repr(seq)
4468         pprint(seq)
4469         with self.assertRaises(ObjNotReady) as err:
4470             seq.encode()
4471         repr(err.exception)
4472         for name, value in non_ready.items():
4473             seq[name] = Boolean(value)
4474         self.assertTrue(seq.ready)
4475         repr(seq)
4476         pprint(seq)
4477
4478     @given(data_strategy())
4479     def test_call(self, d):
4480         class SeqInherited(self.base_klass):
4481             pass
4482         for klass in (self.base_klass, SeqInherited):
4483             (
4484                 value_initial,
4485                 schema_initial,
4486                 impl_initial,
4487                 expl_initial,
4488                 default_initial,
4489                 optional_initial,
4490                 _decoded_initial,
4491             ) = d.draw(seq_values_strategy(seq_klass=klass))
4492             obj_initial = klass(
4493                 value_initial,
4494                 schema_initial,
4495                 impl_initial,
4496                 expl_initial,
4497                 default_initial,
4498                 optional_initial or False,
4499                 _decoded_initial,
4500             )
4501             (
4502                 value,
4503                 _,
4504                 impl,
4505                 expl,
4506                 default,
4507                 optional,
4508                 _decoded,
4509             ) = d.draw(seq_values_strategy(
4510                 seq_klass=klass,
4511                 do_expl=impl_initial is None,
4512             ))
4513             obj = obj_initial(value, impl, expl, default, optional)
4514             value_expected = default if value is None else value
4515             value_expected = (
4516                 default_initial if value_expected is None
4517                 else value_expected
4518             )
4519             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
4520             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4521             self.assertEqual(obj.expl_tag, expl or expl_initial)
4522             self.assertEqual(
4523                 {} if obj.default is None else obj.default._value,
4524                 getattr(default_initial if default is None else default, "_value", {}),
4525             )
4526             if obj.default is None:
4527                 optional = optional_initial if optional is None else optional
4528                 optional = False if optional is None else optional
4529             else:
4530                 optional = True
4531             self.assertEqual(list(obj.specs.items()), schema_initial or [])
4532             self.assertEqual(obj.optional, optional)
4533
4534     @given(data_strategy())
4535     def test_copy(self, d):
4536         class SeqInherited(self.base_klass):
4537             pass
4538         for klass in (self.base_klass, SeqInherited):
4539             values = d.draw(seq_values_strategy(seq_klass=klass))
4540             obj = klass(*values)
4541             obj_copied = obj.copy()
4542             self.assert_copied_basic_fields(obj, obj_copied)
4543             self.assertEqual(obj.specs, obj_copied.specs)
4544             self.assertEqual(obj._value, obj_copied._value)
4545
4546     @given(data_strategy())
4547     def test_stripped(self, d):
4548         value = d.draw(integers())
4549         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4550
4551         class Seq(self.base_klass):
4552             impl = tag_impl
4553             schema = (("whatever", Integer()),)
4554         seq = Seq()
4555         seq["whatever"] = Integer(value)
4556         with self.assertRaises(NotEnoughData):
4557             seq.decode(seq.encode()[:-1])
4558
4559     @given(data_strategy())
4560     def test_stripped_expl(self, d):
4561         value = d.draw(integers())
4562         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4563
4564         class Seq(self.base_klass):
4565             expl = tag_expl
4566             schema = (("whatever", Integer()),)
4567         seq = Seq()
4568         seq["whatever"] = Integer(value)
4569         with self.assertRaises(NotEnoughData):
4570             seq.decode(seq.encode()[:-1])
4571
4572     @given(binary(min_size=2))
4573     def test_non_tag_mismatch_raised(self, junk):
4574         try:
4575             _, _, len_encoded = tag_strip(memoryview(junk))
4576             len_decode(len_encoded)
4577         except Exception:
4578             assume(True)
4579         else:
4580             assume(False)
4581
4582         class Seq(self.base_klass):
4583             schema = (
4584                 ("whatever", Integer()),
4585                 ("junk", Any()),
4586                 ("whenever", Integer()),
4587             )
4588         seq = Seq()
4589         seq["whatever"] = Integer(123)
4590         seq["junk"] = Any(junk)
4591         seq["whenever"] = Integer(123)
4592         with self.assertRaises(DecodeError):
4593             seq.decode(seq.encode())
4594
4595     @given(
4596         integers(min_value=31),
4597         integers(min_value=0),
4598         decode_path_strat,
4599     )
4600     def test_bad_tag(self, tag, offset, decode_path):
4601         with self.assertRaises(DecodeError) as err:
4602             self.base_klass().decode(
4603                 tag_encode(tag)[:-1],
4604                 offset=offset,
4605                 decode_path=decode_path,
4606             )
4607         repr(err.exception)
4608         self.assertEqual(err.exception.offset, offset)
4609         self.assertEqual(err.exception.decode_path, decode_path)
4610
4611     @given(
4612         integers(min_value=128),
4613         integers(min_value=0),
4614         decode_path_strat,
4615     )
4616     def test_bad_len(self, l, offset, decode_path):
4617         with self.assertRaises(DecodeError) as err:
4618             self.base_klass().decode(
4619                 self.base_klass.tag_default + len_encode(l)[:-1],
4620                 offset=offset,
4621                 decode_path=decode_path,
4622             )
4623         repr(err.exception)
4624         self.assertEqual(err.exception.offset, offset)
4625         self.assertEqual(err.exception.decode_path, decode_path)
4626
4627     def _assert_expects(self, seq, expects):
4628         for expect in expects:
4629             self.assertEqual(
4630                 seq.specs[expect["name"]].optional,
4631                 expect["optional"],
4632             )
4633             if expect["default_value"] is not None:
4634                 self.assertEqual(
4635                     seq.specs[expect["name"]].default,
4636                     expect["default_value"],
4637                 )
4638             if expect["presented"]:
4639                 self.assertIn(expect["name"], seq)
4640                 self.assertEqual(seq[expect["name"]], expect["value"])
4641
4642     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4643     @given(data_strategy())
4644     def test_symmetric(self, d):
4645         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
4646         tail_junk = d.draw(binary(max_size=5))
4647         self.assertTrue(seq.ready)
4648         self.assertFalse(seq.decoded)
4649         self._assert_expects(seq, expects)
4650         repr(seq)
4651         pprint(seq)
4652         self.assertTrue(seq.ready)
4653         seq_encoded = seq.encode()
4654         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
4655         self.assertFalse(seq_decoded.lenindef)
4656
4657         t, _, lv = tag_strip(seq_encoded)
4658         _, _, v = len_decode(lv)
4659         seq_encoded_lenindef = t + LENINDEF + v + EOC
4660         seq_decoded_lenindef, tail_lenindef = seq.decode(
4661             seq_encoded_lenindef + tail_junk,
4662             ctx={"bered": True},
4663         )
4664         self.assertTrue(seq_decoded_lenindef.lenindef)
4665         with self.assertRaises(DecodeError):
4666             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
4667         with self.assertRaises(DecodeError):
4668             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
4669         repr(seq_decoded_lenindef)
4670         pprint(seq_decoded_lenindef)
4671         self.assertTrue(seq_decoded_lenindef.ready)
4672
4673         for decoded, decoded_tail, encoded in (
4674                 (seq_decoded, tail, seq_encoded),
4675                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
4676         ):
4677             self.assertEqual(decoded_tail, tail_junk)
4678             self._assert_expects(decoded, expects)
4679             self.assertEqual(seq, decoded)
4680             self.assertEqual(decoded.encode(), seq_encoded)
4681             self.assertEqual(decoded.tlvlen, len(encoded))
4682             for expect in expects:
4683                 if not expect["presented"]:
4684                     self.assertNotIn(expect["name"], decoded)
4685                     continue
4686                 self.assertIn(expect["name"], decoded)
4687                 obj = decoded[expect["name"]]
4688                 self.assertTrue(obj.decoded)
4689                 offset = obj.expl_offset if obj.expled else obj.offset
4690                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4691                 self.assertSequenceEqual(
4692                     seq_encoded[offset:offset + tlvlen],
4693                     obj.encode(),
4694                 )
4695
4696     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4697     @given(data_strategy())
4698     def test_symmetric_with_seq(self, d):
4699         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
4700         self.assertTrue(seq.ready)
4701         seq_encoded = seq.encode()
4702         seq_decoded, tail = seq.decode(seq_encoded)
4703         self.assertEqual(tail, b"")
4704         self.assertTrue(seq.ready)
4705         self.assertEqual(seq, seq_decoded)
4706         self.assertEqual(seq_decoded.encode(), seq_encoded)
4707         for expect_outer in expect_outers:
4708             if not expect_outer["presented"]:
4709                 self.assertNotIn(expect_outer["name"], seq_decoded)
4710                 continue
4711             self.assertIn(expect_outer["name"], seq_decoded)
4712             obj = seq_decoded[expect_outer["name"]]
4713             self.assertTrue(obj.decoded)
4714             offset = obj.expl_offset if obj.expled else obj.offset
4715             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4716             self.assertSequenceEqual(
4717                 seq_encoded[offset:offset + tlvlen],
4718                 obj.encode(),
4719             )
4720             self._assert_expects(obj, expect_outer["expects"])
4721
4722     @given(data_strategy())
4723     def test_default_disappears(self, d):
4724         _schema = list(d.draw(dictionaries(
4725             text_letters(),
4726             sets(integers(), min_size=2, max_size=2),
4727             min_size=1,
4728         )).items())
4729
4730         class Seq(self.base_klass):
4731             schema = [
4732                 (n, Integer(default=d))
4733                 for n, (_, d) in _schema
4734             ]
4735         seq = Seq()
4736         for name, (value, _) in _schema:
4737             seq[name] = Integer(value)
4738         self.assertEqual(len(seq._value), len(_schema))
4739         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
4740         self.assertGreater(len(seq.encode()), len(empty_seq))
4741         for name, (_, default) in _schema:
4742             seq[name] = Integer(default)
4743         self.assertEqual(len(seq._value), 0)
4744         self.assertSequenceEqual(seq.encode(), empty_seq)
4745
4746     @given(data_strategy())
4747     def test_encoded_default_accepted(self, d):
4748         _schema = list(d.draw(dictionaries(
4749             text_letters(),
4750             integers(),
4751             min_size=1,
4752         )).items())
4753         tags = [tag_encode(tag) for tag in d.draw(sets(
4754             integers(min_value=0),
4755             min_size=len(_schema),
4756             max_size=len(_schema),
4757         ))]
4758
4759         class SeqWithoutDefault(self.base_klass):
4760             schema = [
4761                 (n, Integer(impl=t))
4762                 for (n, _), t in zip(_schema, tags)
4763             ]
4764         seq_without_default = SeqWithoutDefault()
4765         for name, value in _schema:
4766             seq_without_default[name] = Integer(value)
4767         seq_encoded = seq_without_default.encode()
4768
4769         class SeqWithDefault(self.base_klass):
4770             schema = [
4771                 (n, Integer(default=v, impl=t))
4772                 for (n, v), t in zip(_schema, tags)
4773             ]
4774         seq_with_default = SeqWithDefault()
4775         seq_decoded, _ = seq_with_default.decode(seq_encoded)
4776         for name, value in _schema:
4777             self.assertEqual(seq_decoded[name], seq_with_default[name])
4778             self.assertEqual(seq_decoded[name], value)
4779
4780     @given(data_strategy())
4781     def test_missing_from_spec(self, d):
4782         names = list(d.draw(sets(text_letters(), min_size=2)))
4783         tags = [tag_encode(tag) for tag in d.draw(sets(
4784             integers(min_value=0),
4785             min_size=len(names),
4786             max_size=len(names),
4787         ))]
4788         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
4789
4790         class SeqFull(self.base_klass):
4791             schema = [(n, Integer(impl=t)) for n, t in names_tags]
4792         seq_full = SeqFull()
4793         for i, name in enumerate(names):
4794             seq_full[name] = Integer(i)
4795         seq_encoded = seq_full.encode()
4796         altered = names_tags[:-2] + names_tags[-1:]
4797
4798         class SeqMissing(self.base_klass):
4799             schema = [(n, Integer(impl=t)) for n, t in altered]
4800         seq_missing = SeqMissing()
4801         with self.assertRaises(TagMismatch):
4802             seq_missing.decode(seq_encoded)
4803
4804
4805 class TestSequence(SeqMixing, CommonMixin, TestCase):
4806     base_klass = Sequence
4807
4808     @given(
4809         integers(),
4810         binary(min_size=1),
4811     )
4812     def test_remaining(self, value, junk):
4813         class Seq(Sequence):
4814             schema = (
4815                 ("whatever", Integer()),
4816             )
4817         int_encoded = Integer(value).encode()
4818         junked = b"".join((
4819             Sequence.tag_default,
4820             len_encode(len(int_encoded + junk)),
4821             int_encoded + junk,
4822         ))
4823         with assertRaisesRegex(self, DecodeError, "remaining"):
4824             Seq().decode(junked)
4825
4826     @given(sets(text_letters(), min_size=2))
4827     def test_obj_unknown(self, names):
4828         missing = names.pop()
4829
4830         class Seq(Sequence):
4831             schema = [(n, Boolean()) for n in names]
4832         seq = Seq()
4833         with self.assertRaises(ObjUnknown) as err:
4834             seq[missing]
4835         repr(err.exception)
4836         with self.assertRaises(ObjUnknown) as err:
4837             seq[missing] = Boolean()
4838         repr(err.exception)
4839
4840     def test_x690_vector(self):
4841         class Seq(Sequence):
4842             schema = (
4843                 ("name", IA5String()),
4844                 ("ok", Boolean()),
4845             )
4846         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
4847         self.assertEqual(seq["name"], "Smith")
4848         self.assertEqual(seq["ok"], True)
4849
4850
4851 class TestSet(SeqMixing, CommonMixin, TestCase):
4852     base_klass = Set
4853
4854     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4855     @given(data_strategy())
4856     def test_sorted(self, d):
4857         tags = [
4858             tag_encode(tag) for tag in
4859             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
4860         ]
4861
4862         class Seq(Set):
4863             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
4864         seq = Seq()
4865         for name, _ in Seq.schema:
4866             seq[name] = OctetString(b"")
4867         seq_encoded = seq.encode()
4868         seq_decoded, _ = seq.decode(seq_encoded)
4869         self.assertSequenceEqual(
4870             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
4871             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
4872         )
4873
4874
4875 @composite
4876 def seqof_values_strategy(draw, schema=None, do_expl=False):
4877     if schema is None:
4878         schema = draw(sampled_from((Boolean(), Integer())))
4879     bound_min, bound_max = sorted(draw(sets(
4880         integers(min_value=0, max_value=10),
4881         min_size=2,
4882         max_size=2,
4883     )))
4884     if isinstance(schema, Boolean):
4885         values_generator = booleans().map(Boolean)
4886     elif isinstance(schema, Integer):
4887         values_generator = integers().map(Integer)
4888     values_generator = lists(
4889         values_generator,
4890         min_size=bound_min,
4891         max_size=bound_max,
4892     )
4893     values = draw(one_of(none(), values_generator))
4894     impl = None
4895     expl = None
4896     if do_expl:
4897         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4898     else:
4899         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4900     default = draw(one_of(none(), values_generator))
4901     optional = draw(one_of(none(), booleans()))
4902     _decoded = (
4903         draw(integers(min_value=0)),
4904         draw(integers(min_value=0)),
4905         draw(integers(min_value=0)),
4906     )
4907     return (
4908         schema,
4909         values,
4910         (bound_min, bound_max),
4911         impl,
4912         expl,
4913         default,
4914         optional,
4915         _decoded,
4916     )
4917
4918
4919 class SeqOfMixing(object):
4920     def test_invalid_value_type(self):
4921         with self.assertRaises(InvalidValueType) as err:
4922             self.base_klass(123)
4923         repr(err.exception)
4924
4925     def test_invalid_values_type(self):
4926         class SeqOf(self.base_klass):
4927             schema = Integer()
4928         with self.assertRaises(InvalidValueType) as err:
4929             SeqOf([Integer(123), Boolean(False), Integer(234)])
4930         repr(err.exception)
4931
4932     def test_schema_required(self):
4933         with assertRaisesRegex(self, ValueError, "schema must be specified"):
4934             self.base_klass.__mro__[1]()
4935
4936     @given(booleans(), booleans(), binary(), binary())
4937     def test_comparison(self, value1, value2, tag1, tag2):
4938         class SeqOf(self.base_klass):
4939             schema = Boolean()
4940         obj1 = SeqOf([Boolean(value1)])
4941         obj2 = SeqOf([Boolean(value2)])
4942         self.assertEqual(obj1 == obj2, value1 == value2)
4943         self.assertEqual(obj1 != obj2, value1 != value2)
4944         self.assertEqual(obj1 == list(obj2), value1 == value2)
4945         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
4946         obj1 = SeqOf([Boolean(value1)], impl=tag1)
4947         obj2 = SeqOf([Boolean(value1)], impl=tag2)
4948         self.assertEqual(obj1 == obj2, tag1 == tag2)
4949         self.assertEqual(obj1 != obj2, tag1 != tag2)
4950
4951     @given(lists(booleans()))
4952     def test_iter(self, values):
4953         class SeqOf(self.base_klass):
4954             schema = Boolean()
4955         obj = SeqOf([Boolean(value) for value in values])
4956         self.assertEqual(len(obj), len(values))
4957         for i, value in enumerate(obj):
4958             self.assertEqual(value, values[i])
4959
4960     @given(data_strategy())
4961     def test_ready(self, d):
4962         ready = [Integer(v) for v in d.draw(lists(
4963             integers(),
4964             min_size=1,
4965             max_size=3,
4966         ))]
4967         non_ready = [
4968             Integer() for _ in
4969             range(d.draw(integers(min_value=1, max_value=5)))
4970         ]
4971
4972         class SeqOf(self.base_klass):
4973             schema = Integer()
4974         values = d.draw(permutations(ready + non_ready))
4975         seqof = SeqOf()
4976         for value in values:
4977             seqof.append(value)
4978         self.assertFalse(seqof.ready)
4979         repr(seqof)
4980         pprint(seqof)
4981         with self.assertRaises(ObjNotReady) as err:
4982             seqof.encode()
4983         repr(err.exception)
4984         for i, value in enumerate(values):
4985             self.assertEqual(seqof[i], value)
4986             if not seqof[i].ready:
4987                 seqof[i] = Integer(i)
4988         self.assertTrue(seqof.ready)
4989         repr(seqof)
4990         pprint(seqof)
4991
4992     def test_spec_mismatch(self):
4993         class SeqOf(self.base_klass):
4994             schema = Integer()
4995         seqof = SeqOf()
4996         seqof.append(Integer(123))
4997         with self.assertRaises(ValueError):
4998             seqof.append(Boolean(False))
4999         with self.assertRaises(ValueError):
5000             seqof[0] = Boolean(False)
5001
5002     @given(data_strategy())
5003     def test_bounds_satisfied(self, d):
5004         class SeqOf(self.base_klass):
5005             schema = Boolean()
5006         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
5007         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5008         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
5009         SeqOf(value=value, bounds=(bound_min, bound_max))
5010
5011     @given(data_strategy())
5012     def test_bounds_unsatisfied(self, d):
5013         class SeqOf(self.base_klass):
5014             schema = Boolean()
5015         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
5016         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5017         value = [Boolean()] * d.draw(integers(max_value=bound_min - 1))
5018         with self.assertRaises(BoundsError) as err:
5019             SeqOf(value=value, bounds=(bound_min, bound_max))
5020         repr(err.exception)
5021         value = [Boolean()] * d.draw(integers(
5022             min_value=bound_max + 1,
5023             max_value=bound_max + 10,
5024         ))
5025         with self.assertRaises(BoundsError) as err:
5026             SeqOf(value=value, bounds=(bound_min, bound_max))
5027         repr(err.exception)
5028
5029     @given(integers(min_value=1, max_value=10))
5030     def test_out_of_bounds(self, bound_max):
5031         class SeqOf(self.base_klass):
5032             schema = Integer()
5033             bounds = (0, bound_max)
5034         seqof = SeqOf()
5035         for _ in range(bound_max):
5036             seqof.append(Integer(123))
5037         with self.assertRaises(BoundsError):
5038             seqof.append(Integer(123))
5039
5040     @given(data_strategy())
5041     def test_call(self, d):
5042         (
5043             schema_initial,
5044             value_initial,
5045             bounds_initial,
5046             impl_initial,
5047             expl_initial,
5048             default_initial,
5049             optional_initial,
5050             _decoded_initial,
5051         ) = d.draw(seqof_values_strategy())
5052
5053         class SeqOf(self.base_klass):
5054             schema = schema_initial
5055         obj_initial = SeqOf(
5056             value=value_initial,
5057             bounds=bounds_initial,
5058             impl=impl_initial,
5059             expl=expl_initial,
5060             default=default_initial,
5061             optional=optional_initial or False,
5062             _decoded=_decoded_initial,
5063         )
5064         (
5065             _,
5066             value,
5067             bounds,
5068             impl,
5069             expl,
5070             default,
5071             optional,
5072             _decoded,
5073         ) = d.draw(seqof_values_strategy(
5074             schema=schema_initial,
5075             do_expl=impl_initial is None,
5076         ))
5077         if (default is None) and (obj_initial.default is not None):
5078             bounds = None
5079         if (
5080                 (bounds is None) and
5081                 (value is not None) and
5082                 (bounds_initial is not None) and
5083                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
5084         ):
5085             value = None
5086         if (
5087                 (bounds is None) and
5088                 (default is not None) and
5089                 (bounds_initial is not None) and
5090                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
5091         ):
5092             default = None
5093         obj = obj_initial(
5094             value=value,
5095             bounds=bounds,
5096             impl=impl,
5097             expl=expl,
5098             default=default,
5099             optional=optional,
5100         )
5101         if obj.ready:
5102             value_expected = default if value is None else value
5103             value_expected = (
5104                 default_initial if value_expected is None
5105                 else value_expected
5106             )
5107             value_expected = () if value_expected is None else value_expected
5108             self.assertEqual(obj, value_expected)
5109         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5110         self.assertEqual(obj.expl_tag, expl or expl_initial)
5111         self.assertEqual(
5112             obj.default,
5113             default_initial if default is None else default,
5114         )
5115         if obj.default is None:
5116             optional = optional_initial if optional is None else optional
5117             optional = False if optional is None else optional
5118         else:
5119             optional = True
5120         self.assertEqual(obj.optional, optional)
5121         self.assertEqual(
5122             (obj._bound_min, obj._bound_max),
5123             bounds or bounds_initial or (0, float("+inf")),
5124         )
5125
5126     @given(seqof_values_strategy())
5127     def test_copy(self, values):
5128         _schema, value, bounds, impl, expl, default, optional, _decoded = values
5129
5130         class SeqOf(self.base_klass):
5131             schema = _schema
5132         obj = SeqOf(
5133             value=value,
5134             bounds=bounds,
5135             impl=impl,
5136             expl=expl,
5137             default=default,
5138             optional=optional or False,
5139             _decoded=_decoded,
5140         )
5141         obj_copied = obj.copy()
5142         self.assert_copied_basic_fields(obj, obj_copied)
5143         self.assertEqual(obj._bound_min, obj_copied._bound_min)
5144         self.assertEqual(obj._bound_max, obj_copied._bound_max)
5145         self.assertEqual(obj._value, obj_copied._value)
5146
5147     @given(
5148         lists(binary()),
5149         integers(min_value=1).map(tag_encode),
5150     )
5151     def test_stripped(self, values, tag_impl):
5152         class SeqOf(self.base_klass):
5153             schema = OctetString()
5154         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
5155         with self.assertRaises(NotEnoughData):
5156             obj.decode(obj.encode()[:-1])
5157
5158     @given(
5159         lists(binary()),
5160         integers(min_value=1).map(tag_ctxc),
5161     )
5162     def test_stripped_expl(self, values, tag_expl):
5163         class SeqOf(self.base_klass):
5164             schema = OctetString()
5165         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
5166         with self.assertRaises(NotEnoughData):
5167             obj.decode(obj.encode()[:-1])
5168
5169     @given(
5170         integers(min_value=31),
5171         integers(min_value=0),
5172         decode_path_strat,
5173     )
5174     def test_bad_tag(self, tag, offset, decode_path):
5175         with self.assertRaises(DecodeError) as err:
5176             self.base_klass().decode(
5177                 tag_encode(tag)[:-1],
5178                 offset=offset,
5179                 decode_path=decode_path,
5180             )
5181         repr(err.exception)
5182         self.assertEqual(err.exception.offset, offset)
5183         self.assertEqual(err.exception.decode_path, decode_path)
5184
5185     @given(
5186         integers(min_value=128),
5187         integers(min_value=0),
5188         decode_path_strat,
5189     )
5190     def test_bad_len(self, l, offset, decode_path):
5191         with self.assertRaises(DecodeError) as err:
5192             self.base_klass().decode(
5193                 self.base_klass.tag_default + len_encode(l)[:-1],
5194                 offset=offset,
5195                 decode_path=decode_path,
5196             )
5197         repr(err.exception)
5198         self.assertEqual(err.exception.offset, offset)
5199         self.assertEqual(err.exception.decode_path, decode_path)
5200
5201     @given(binary(min_size=1))
5202     def test_tag_mismatch(self, impl):
5203         assume(impl != self.base_klass.tag_default)
5204         with self.assertRaises(TagMismatch):
5205             self.base_klass(impl=impl).decode(self.base_klass().encode())
5206
5207     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5208     @given(
5209         seqof_values_strategy(schema=Integer()),
5210         lists(integers().map(Integer)),
5211         integers(min_value=1).map(tag_ctxc),
5212         integers(min_value=0),
5213         binary(max_size=5),
5214     )
5215     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
5216         _, _, _, _, _, default, optional, _decoded = values
5217
5218         class SeqOf(self.base_klass):
5219             schema = Integer()
5220         obj = SeqOf(
5221             value=value,
5222             default=default,
5223             optional=optional,
5224             _decoded=_decoded,
5225         )
5226         repr(obj)
5227         pprint(obj)
5228         self.assertFalse(obj.expled)
5229         obj_encoded = obj.encode()
5230         obj_expled = obj(value, expl=tag_expl)
5231         self.assertTrue(obj_expled.expled)
5232         repr(obj_expled)
5233         pprint(obj_expled)
5234         obj_expled_encoded = obj_expled.encode()
5235         obj_decoded, tail = obj_expled.decode(
5236             obj_expled_encoded + tail_junk,
5237             offset=offset,
5238         )
5239         repr(obj_decoded)
5240         pprint(obj_decoded)
5241         self.assertEqual(tail, tail_junk)
5242         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
5243         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5244         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5245         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5246         self.assertEqual(
5247             obj_decoded.expl_llen,
5248             len(len_encode(len(obj_encoded))),
5249         )
5250         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5251         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5252         self.assertEqual(
5253             obj_decoded.offset,
5254             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5255         )
5256         self.assertEqual(obj_decoded.expl_offset, offset)
5257         for obj_inner in obj_decoded:
5258             self.assertIn(obj_inner, obj_decoded)
5259             self.assertSequenceEqual(
5260                 obj_inner.encode(),
5261                 obj_expled_encoded[
5262                     obj_inner.offset - offset:
5263                     obj_inner.offset + obj_inner.tlvlen - offset
5264                 ],
5265             )
5266
5267         t, _, lv = tag_strip(obj_encoded)
5268         _, _, v = len_decode(lv)
5269         obj_encoded_lenindef = t + LENINDEF + v + EOC
5270         obj_decoded_lenindef, tail_lenindef = obj.decode(
5271             obj_encoded_lenindef + tail_junk,
5272             ctx={"bered": True},
5273         )
5274         self.assertTrue(obj_decoded_lenindef.lenindef)
5275         repr(obj_decoded_lenindef)
5276         pprint(obj_decoded_lenindef)
5277         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
5278         with self.assertRaises(DecodeError):
5279             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
5280         with self.assertRaises(DecodeError):
5281             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
5282
5283
5284 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
5285     class SeqOf(SequenceOf):
5286         schema = "whatever"
5287     base_klass = SeqOf
5288
5289     def _test_symmetric_compare_objs(self, obj1, obj2):
5290         self.assertEqual(obj1, obj2)
5291         self.assertSequenceEqual(list(obj1), list(obj2))
5292
5293
5294 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
5295     class SeqOf(SetOf):
5296         schema = "whatever"
5297     base_klass = SeqOf
5298
5299     def _test_symmetric_compare_objs(self, obj1, obj2):
5300         self.assertSetEqual(
5301             set(int(v) for v in obj1),
5302             set(int(v) for v in obj2),
5303         )
5304
5305     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5306     @given(data_strategy())
5307     def test_sorted(self, d):
5308         values = [OctetString(v) for v in d.draw(lists(binary()))]
5309
5310         class Seq(SetOf):
5311             schema = OctetString()
5312         seq = Seq(values)
5313         seq_encoded = seq.encode()
5314         seq_decoded, _ = seq.decode(seq_encoded)
5315         self.assertSequenceEqual(
5316             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5317             b"".join(sorted([v.encode() for v in values])),
5318         )
5319
5320
5321 class TestGoMarshalVectors(TestCase):
5322     def runTest(self):
5323         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
5324         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
5325         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
5326         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
5327         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
5328
5329         class Seq(Sequence):
5330             schema = (
5331                 ("erste", Integer()),
5332                 ("zweite", Integer(optional=True))
5333             )
5334         seq = Seq()
5335         seq["erste"] = Integer(64)
5336         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5337         seq["erste"] = Integer(0x123456)
5338         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
5339         seq["erste"] = Integer(64)
5340         seq["zweite"] = Integer(65)
5341         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
5342
5343         class NestedSeq(Sequence):
5344             schema = (
5345                 ("nest", Seq()),
5346             )
5347         seq["erste"] = Integer(127)
5348         seq["zweite"] = None
5349         nested = NestedSeq()
5350         nested["nest"] = seq
5351         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
5352
5353         self.assertSequenceEqual(
5354             OctetString(b"\x01\x02\x03").encode(),
5355             hexdec("0403010203"),
5356         )
5357
5358         class Seq(Sequence):
5359             schema = (
5360                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
5361             )
5362         seq = Seq()
5363         seq["erste"] = Integer(64)
5364         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
5365
5366         class Seq(Sequence):
5367             schema = (
5368                 ("erste", Integer(expl=tag_ctxc(5))),
5369             )
5370         seq = Seq()
5371         seq["erste"] = Integer(64)
5372         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
5373
5374         class Seq(Sequence):
5375             schema = (
5376                 ("erste", Null(
5377                     impl=tag_encode(0, klass=TagClassContext),
5378                     optional=True,
5379                 )),
5380             )
5381         seq = Seq()
5382         seq["erste"] = Null()
5383         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
5384         seq["erste"] = None
5385         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5386
5387         self.assertSequenceEqual(
5388             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
5389             hexdec("170d3730303130313030303030305a"),
5390         )
5391         self.assertSequenceEqual(
5392             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
5393             hexdec("170d3039313131353232353631365a"),
5394         )
5395         self.assertSequenceEqual(
5396             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
5397             hexdec("180f32313030303430353132303130315a"),
5398         )
5399
5400         class Seq(Sequence):
5401             schema = (
5402                 ("erste", GeneralizedTime()),
5403             )
5404         seq = Seq()
5405         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
5406         self.assertSequenceEqual(
5407             seq.encode(),
5408             hexdec("3011180f32303039313131353232353631365a"),
5409         )
5410
5411         self.assertSequenceEqual(
5412             BitString((1, b"\x80")).encode(),
5413             hexdec("03020780"),
5414         )
5415         self.assertSequenceEqual(
5416             BitString((12, b"\x81\xF0")).encode(),
5417             hexdec("03030481f0"),
5418         )
5419
5420         self.assertSequenceEqual(
5421             ObjectIdentifier("1.2.3.4").encode(),
5422             hexdec("06032a0304"),
5423         )
5424         self.assertSequenceEqual(
5425             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
5426             hexdec("06092a864888932d010105"),
5427         )
5428         self.assertSequenceEqual(
5429             ObjectIdentifier("2.100.3").encode(),
5430             hexdec("0603813403"),
5431         )
5432
5433         self.assertSequenceEqual(
5434             PrintableString("test").encode(),
5435             hexdec("130474657374"),
5436         )
5437         self.assertSequenceEqual(
5438             PrintableString("x" * 127).encode(),
5439             hexdec("137F" + "78" * 127),
5440         )
5441         self.assertSequenceEqual(
5442             PrintableString("x" * 128).encode(),
5443             hexdec("138180" + "78" * 128),
5444         )
5445         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
5446
5447         class Seq(Sequence):
5448             schema = (
5449                 ("erste", IA5String()),
5450             )
5451         seq = Seq()
5452         seq["erste"] = IA5String("test")
5453         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
5454
5455         class Seq(Sequence):
5456             schema = (
5457                 ("erste", PrintableString()),
5458             )
5459         seq = Seq()
5460         seq["erste"] = PrintableString("test")
5461         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
5462         seq["erste"] = PrintableString("test*")
5463         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
5464
5465         class Seq(Sequence):
5466             schema = (
5467                 ("erste", Any(optional=True)),
5468                 ("zweite", Integer()),
5469             )
5470         seq = Seq()
5471         seq["zweite"] = Integer(64)
5472         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5473
5474         class Seq(SetOf):
5475             schema = Integer()
5476         seq = Seq()
5477         seq.append(Integer(10))
5478         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
5479
5480         class _SeqOf(SequenceOf):
5481             schema = PrintableString()
5482
5483         class SeqOf(SequenceOf):
5484             schema = _SeqOf()
5485         _seqof = _SeqOf()
5486         _seqof.append(PrintableString("1"))
5487         seqof = SeqOf()
5488         seqof.append(_seqof)
5489         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
5490
5491         class Seq(Sequence):
5492             schema = (
5493                 ("erste", Integer(default=1)),
5494             )
5495         seq = Seq()
5496         seq["erste"] = Integer(0)
5497         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
5498         seq["erste"] = Integer(1)
5499         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5500         seq["erste"] = Integer(2)
5501         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
5502
5503
5504 class TestPP(TestCase):
5505     @given(data_strategy())
5506     def test_oid_printing(self, d):
5507         oids = {
5508             str(ObjectIdentifier(k)): v * 2
5509             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
5510         }
5511         chosen = d.draw(sampled_from(sorted(oids)))
5512         chosen_id = oids[chosen]
5513         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
5514         self.assertNotIn(chosen_id, pp_console_row(pp))
5515         self.assertIn(chosen_id, pp_console_row(pp, oids=oids))
5516
5517
5518 class TestAutoAddSlots(TestCase):
5519     def runTest(self):
5520         class Inher(Integer):
5521             pass
5522
5523         with self.assertRaises(AttributeError):
5524             inher = Inher()
5525             inher.unexistent = "whatever"
5526
5527
5528 class TestOIDDefines(TestCase):
5529     @given(data_strategy())
5530     def runTest(self, d):
5531         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
5532         value_name_chosen = d.draw(sampled_from(value_names))
5533         oids = [
5534             ObjectIdentifier(oid)
5535             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
5536         ]
5537         oid_chosen = d.draw(sampled_from(oids))
5538         values = d.draw(lists(
5539             integers(),
5540             min_size=len(value_names),
5541             max_size=len(value_names),
5542         ))
5543         _schema = [
5544             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
5545                 oid: Integer() for oid in oids[:-1]
5546             }),))),
5547         ]
5548         for i, value_name in enumerate(value_names):
5549             _schema.append((value_name, Any(expl=tag_ctxp(i))))
5550
5551         class Seq(Sequence):
5552             schema = _schema
5553         seq = Seq()
5554         for value_name, value in zip(value_names, values):
5555             seq[value_name] = Any(Integer(value).encode())
5556         seq["type"] = oid_chosen
5557         seq, _ = Seq().decode(seq.encode())
5558         for value_name in value_names:
5559             if value_name == value_name_chosen:
5560                 continue
5561             self.assertIsNone(seq[value_name].defined)
5562         if value_name_chosen in oids[:-1]:
5563             self.assertIsNotNone(seq[value_name_chosen].defined)
5564             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
5565             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
5566
5567
5568 class TestDefinesByPath(TestCase):
5569     def test_generated(self):
5570         class Seq(Sequence):
5571             schema = (
5572                 ("type", ObjectIdentifier()),
5573                 ("value", OctetString(expl=tag_ctxc(123))),
5574             )
5575
5576         class SeqInner(Sequence):
5577             schema = (
5578                 ("typeInner", ObjectIdentifier()),
5579                 ("valueInner", Any()),
5580             )
5581
5582         class PairValue(SetOf):
5583             schema = Any()
5584
5585         class Pair(Sequence):
5586             schema = (
5587                 ("type", ObjectIdentifier()),
5588                 ("value", PairValue()),
5589             )
5590
5591         class Pairs(SequenceOf):
5592             schema = Pair()
5593
5594         (
5595             type_integered,
5596             type_sequenced,
5597             type_innered,
5598             type_octet_stringed,
5599         ) = [
5600             ObjectIdentifier(oid)
5601             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
5602         ]
5603         seq_integered = Seq()
5604         seq_integered["type"] = type_integered
5605         seq_integered["value"] = OctetString(Integer(123).encode())
5606         seq_integered_raw = seq_integered.encode()
5607
5608         pairs = Pairs()
5609         pairs_input = (
5610             (type_octet_stringed, OctetString(b"whatever")),
5611             (type_integered, Integer(123)),
5612             (type_octet_stringed, OctetString(b"whenever")),
5613             (type_integered, Integer(234)),
5614         )
5615         for t, v in pairs_input:
5616             pair = Pair()
5617             pair["type"] = t
5618             pair["value"] = PairValue((Any(v),))
5619             pairs.append(pair)
5620         seq_inner = SeqInner()
5621         seq_inner["typeInner"] = type_innered
5622         seq_inner["valueInner"] = Any(pairs)
5623         seq_sequenced = Seq()
5624         seq_sequenced["type"] = type_sequenced
5625         seq_sequenced["value"] = OctetString(seq_inner.encode())
5626         seq_sequenced_raw = seq_sequenced.encode()
5627
5628         defines_by_path = []
5629         seq_integered, _ = Seq().decode(seq_integered_raw)
5630         self.assertIsNone(seq_integered["value"].defined)
5631         defines_by_path.append(
5632             (("type",), ((("value",), {
5633                 type_integered: Integer(),
5634                 type_sequenced: SeqInner(),
5635             }),))
5636         )
5637         seq_integered, _ = Seq().decode(
5638             seq_integered_raw,
5639             ctx={"defines_by_path": defines_by_path},
5640         )
5641         self.assertIsNotNone(seq_integered["value"].defined)
5642         self.assertEqual(seq_integered["value"].defined[0], type_integered)
5643         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
5644         self.assertTrue(seq_integered_raw[
5645             seq_integered["value"].defined[1].offset:
5646         ].startswith(Integer(123).encode()))
5647
5648         seq_sequenced, _ = Seq().decode(
5649             seq_sequenced_raw,
5650             ctx={"defines_by_path": defines_by_path},
5651         )
5652         self.assertIsNotNone(seq_sequenced["value"].defined)
5653         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5654         seq_inner = seq_sequenced["value"].defined[1]
5655         self.assertIsNone(seq_inner["valueInner"].defined)
5656
5657         defines_by_path.append((
5658             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
5659             ((("valueInner",), {type_innered: Pairs()}),),
5660         ))
5661         seq_sequenced, _ = Seq().decode(
5662             seq_sequenced_raw,
5663             ctx={"defines_by_path": defines_by_path},
5664         )
5665         self.assertIsNotNone(seq_sequenced["value"].defined)
5666         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5667         seq_inner = seq_sequenced["value"].defined[1]
5668         self.assertIsNotNone(seq_inner["valueInner"].defined)
5669         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5670         pairs = seq_inner["valueInner"].defined[1]
5671         for pair in pairs:
5672             self.assertIsNone(pair["value"][0].defined)
5673
5674         defines_by_path.append((
5675             (
5676                 "value",
5677                 DecodePathDefBy(type_sequenced),
5678                 "valueInner",
5679                 DecodePathDefBy(type_innered),
5680                 any,
5681                 "type",
5682             ),
5683             ((("value",), {
5684                 type_integered: Integer(),
5685                 type_octet_stringed: OctetString(),
5686             }),),
5687         ))
5688         seq_sequenced, _ = Seq().decode(
5689             seq_sequenced_raw,
5690             ctx={"defines_by_path": defines_by_path},
5691         )
5692         self.assertIsNotNone(seq_sequenced["value"].defined)
5693         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5694         seq_inner = seq_sequenced["value"].defined[1]
5695         self.assertIsNotNone(seq_inner["valueInner"].defined)
5696         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5697         pairs_got = seq_inner["valueInner"].defined[1]
5698         for pair_input, pair_got in zip(pairs_input, pairs_got):
5699             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
5700             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
5701
5702     @given(oid_strategy(), integers())
5703     def test_simple(self, oid, tgt):
5704         class Inner(Sequence):
5705             schema = (
5706                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
5707                     ObjectIdentifier(oid): Integer(),
5708                 }),))),
5709             )
5710
5711         class Outer(Sequence):
5712             schema = (
5713                 ("inner", Inner()),
5714                 ("tgt", OctetString()),
5715             )
5716
5717         inner = Inner()
5718         inner["oid"] = ObjectIdentifier(oid)
5719         outer = Outer()
5720         outer["inner"] = inner
5721         outer["tgt"] = OctetString(Integer(tgt).encode())
5722         decoded, _ = Outer().decode(outer.encode())
5723         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
5724
5725
5726 class TestAbsDecodePath(TestCase):
5727     @given(
5728         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5729         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5730     )
5731     def test_concat(self, decode_path, rel_path):
5732         self.assertSequenceEqual(
5733             abs_decode_path(decode_path, rel_path),
5734             decode_path + rel_path,
5735         )
5736
5737     @given(
5738         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5739         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5740     )
5741     def test_abs(self, decode_path, rel_path):
5742         self.assertSequenceEqual(
5743             abs_decode_path(decode_path, ("/",) + rel_path),
5744             rel_path,
5745         )
5746
5747     @given(
5748         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
5749         integers(min_value=1, max_value=3),
5750         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5751     )
5752     def test_dots(self, decode_path, number_of_dots, rel_path):
5753         self.assertSequenceEqual(
5754             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
5755             decode_path[:-number_of_dots] + rel_path,
5756         )
5757
5758
5759 class TestStrictDefaultExistence(TestCase):
5760     @given(data_strategy())
5761     def runTest(self, d):
5762         count = d.draw(integers(min_value=1, max_value=10))
5763         chosen = d.draw(integers(min_value=0, max_value=count - 1))
5764         _schema = [
5765             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
5766             for i in range(count)
5767         ]
5768
5769         class Seq(Sequence):
5770             schema = _schema
5771         seq = Seq()
5772         for i in range(count):
5773             seq["int%d" % i] = Integer(123)
5774         raw = seq.encode()
5775         chosen = "int%d" % chosen
5776         seq.specs[chosen] = seq.specs[chosen](default=123)
5777         seq.decode(raw)
5778         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5779             seq.decode(raw, ctx={"strict_default_existence": True})
5780
5781
5782 class TestX690PrefixedType(TestCase):
5783     def runTest(self):
5784         self.assertSequenceEqual(
5785             VisibleString("Jones").encode(),
5786             hexdec("1A054A6F6E6573"),
5787         )
5788         self.assertSequenceEqual(
5789             VisibleString(
5790                 "Jones",
5791                 impl=tag_encode(3, klass=TagClassApplication),
5792             ).encode(),
5793             hexdec("43054A6F6E6573"),
5794         )
5795         self.assertSequenceEqual(
5796             Any(
5797                 VisibleString(
5798                     "Jones",
5799                     impl=tag_encode(3, klass=TagClassApplication),
5800                 ),
5801                 expl=tag_ctxc(2),
5802             ).encode(),
5803             hexdec("A20743054A6F6E6573"),
5804         )
5805         self.assertSequenceEqual(
5806             OctetString(
5807                 VisibleString(
5808                     "Jones",
5809                     impl=tag_encode(3, klass=TagClassApplication),
5810                 ).encode(),
5811                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
5812             ).encode(),
5813             hexdec("670743054A6F6E6573"),
5814         )
5815         self.assertSequenceEqual(
5816             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
5817             hexdec("82054A6F6E6573"),
5818         )