]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
BER additional tests
[pyderasn.git] / tests / test_pyderasn.py
1 # coding: utf-8
2 # PyDERASN -- Python ASN.1 DER/BER codec with abstract structures
3 # Copyright (C) 2017-2018 Sergey Matveev <stargrave@stargrave.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Lesser General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU Lesser General Public License for more details.
14 #
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this program.  If not, see
17 # <http://www.gnu.org/licenses/>.
18
19 from datetime import datetime
20 from string import ascii_letters
21 from string import digits
22 from string import printable
23 from string import whitespace
24 from unittest import TestCase
25
26 from hypothesis import assume
27 from hypothesis import given
28 from hypothesis import settings
29 from hypothesis.strategies import binary
30 from hypothesis.strategies import booleans
31 from hypothesis.strategies import composite
32 from hypothesis.strategies import data as data_strategy
33 from hypothesis.strategies import datetimes
34 from hypothesis.strategies import dictionaries
35 from hypothesis.strategies import integers
36 from hypothesis.strategies import just
37 from hypothesis.strategies import lists
38 from hypothesis.strategies import none
39 from hypothesis.strategies import one_of
40 from hypothesis.strategies import permutations
41 from hypothesis.strategies import sampled_from
42 from hypothesis.strategies import sets
43 from hypothesis.strategies import text
44 from hypothesis.strategies import tuples
45 from six import assertRaisesRegex
46 from six import binary_type
47 from six import byte2int
48 from six import indexbytes
49 from six import int2byte
50 from six import iterbytes
51 from six import PY2
52 from six import text_type
53 from six import unichr as six_unichr
54
55 from pyderasn import _pp
56 from pyderasn import abs_decode_path
57 from pyderasn import Any
58 from pyderasn import BitString
59 from pyderasn import BMPString
60 from pyderasn import Boolean
61 from pyderasn import BoundsError
62 from pyderasn import Choice
63 from pyderasn import DecodeError
64 from pyderasn import DecodePathDefBy
65 from pyderasn import Enumerated
66 from pyderasn import EOC
67 from pyderasn import EOC_LEN
68 from pyderasn import GeneralizedTime
69 from pyderasn import GeneralString
70 from pyderasn import GraphicString
71 from pyderasn import hexdec
72 from pyderasn import hexenc
73 from pyderasn import IA5String
74 from pyderasn import Integer
75 from pyderasn import InvalidLength
76 from pyderasn import InvalidOID
77 from pyderasn import InvalidValueType
78 from pyderasn import len_decode
79 from pyderasn import len_encode
80 from pyderasn import NotEnoughData
81 from pyderasn import Null
82 from pyderasn import NumericString
83 from pyderasn import ObjectIdentifier
84 from pyderasn import ObjNotReady
85 from pyderasn import ObjUnknown
86 from pyderasn import OctetString
87 from pyderasn import pp_console_row
88 from pyderasn import pprint
89 from pyderasn import PrintableString
90 from pyderasn import Sequence
91 from pyderasn import SequenceOf
92 from pyderasn import Set
93 from pyderasn import SetOf
94 from pyderasn import tag_ctxc
95 from pyderasn import tag_ctxp
96 from pyderasn import tag_decode
97 from pyderasn import tag_encode
98 from pyderasn import tag_strip
99 from pyderasn import TagClassApplication
100 from pyderasn import TagClassContext
101 from pyderasn import TagClassPrivate
102 from pyderasn import TagClassUniversal
103 from pyderasn import TagFormConstructed
104 from pyderasn import TagFormPrimitive
105 from pyderasn import TagMismatch
106 from pyderasn import TeletexString
107 from pyderasn import UniversalString
108 from pyderasn import UTCTime
109 from pyderasn import UTF8String
110 from pyderasn import VideotexString
111 from pyderasn import VisibleString
112
113
114 settings.register_profile("local", settings(
115     deadline=5000,
116     perform_health_check=False,
117 ))
118 settings.load_profile("local")
119 LONG_TEST_MAX_EXAMPLES = settings().max_examples * 4
120
121 tag_classes = sampled_from((
122     TagClassApplication,
123     TagClassContext,
124     TagClassPrivate,
125     TagClassUniversal,
126 ))
127 tag_forms = sampled_from((TagFormConstructed, TagFormPrimitive))
128 decode_path_strat = lists(integers(), max_size=3).map(
129     lambda decode_path: tuple(str(dp) for dp in decode_path)
130 )
131
132
133 class TestHex(TestCase):
134     @given(binary())
135     def test_symmetric(self, data):
136         self.assertEqual(hexdec(hexenc(data)), data)
137
138
139 class TestTagCoder(TestCase):
140     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
141     @given(
142         tag_classes,
143         tag_forms,
144         integers(min_value=0, max_value=30),
145         binary(max_size=5),
146     )
147     def test_short(self, klass, form, num, junk):
148         raw = tag_encode(klass=klass, form=form, num=num)
149         self.assertEqual(tag_decode(raw), (klass, form, num))
150         self.assertEqual(len(raw), 1)
151         self.assertEqual(
152             byte2int(tag_encode(klass=klass, form=form, num=0)),
153             byte2int(raw) & (1 << 7 | 1 << 6 | 1 << 5),
154         )
155         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
156         self.assertSequenceEqual(stripped.tobytes(), raw)
157         self.assertEqual(tlen, len(raw))
158         self.assertSequenceEqual(tail, junk)
159
160     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
161     @given(
162         tag_classes,
163         tag_forms,
164         integers(min_value=31),
165         binary(max_size=5),
166     )
167     def test_long(self, klass, form, num, junk):
168         raw = tag_encode(klass=klass, form=form, num=num)
169         self.assertEqual(tag_decode(raw), (klass, form, num))
170         self.assertGreater(len(raw), 1)
171         self.assertEqual(
172             byte2int(tag_encode(klass=klass, form=form, num=0)) | 31,
173             byte2int(raw[:1]),
174         )
175         self.assertEqual(byte2int(raw[-1:]) & 0x80, 0)
176         self.assertTrue(all(b & 0x80 > 0 for b in iterbytes(raw[1:-1])))
177         stripped, tlen, tail = tag_strip(memoryview(raw + junk))
178         self.assertSequenceEqual(stripped.tobytes(), raw)
179         self.assertEqual(tlen, len(raw))
180         self.assertSequenceEqual(tail, junk)
181
182     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
183     @given(integers(min_value=31))
184     def test_unfinished_tag(self, num):
185         raw = bytearray(tag_encode(num=num))
186         for i in range(1, len(raw)):
187             raw[i] |= 0x80
188         with assertRaisesRegex(self, DecodeError, "unfinished tag"):
189             tag_strip(bytes(raw))
190
191     def test_go_vectors_valid(self):
192         for data, (eklass, etag, elen, eform) in (
193                 (b"\x80\x01", (TagClassContext, 0, 1, TagFormPrimitive)),
194                 (b"\xa0\x01", (TagClassContext, 0, 1, TagFormConstructed)),
195                 (b"\x02\x00", (TagClassUniversal, 2, 0, TagFormPrimitive)),
196                 (b"\xfe\x00", (TagClassPrivate, 30, 0, TagFormConstructed)),
197                 (b"\x1f\x1f\x00", (TagClassUniversal, 31, 0, TagFormPrimitive)),
198                 (b"\x1f\x81\x00\x00", (TagClassUniversal, 128, 0, TagFormPrimitive)),
199                 (b"\x1f\x81\x80\x01\x00", (TagClassUniversal, 0x4001, 0, TagFormPrimitive)),
200                 (b"\x00\x81\x80", (TagClassUniversal, 0, 128, TagFormPrimitive)),
201                 (b"\x00\x82\x01\x00", (TagClassUniversal, 0, 256, TagFormPrimitive)),
202                 (b"\xa0\x84\x7f\xff\xff\xff", (TagClassContext, 0, 0x7fffffff, TagFormConstructed)),
203         ):
204             tag, _, len_encoded = tag_strip(memoryview(data))
205             klass, form, num = tag_decode(tag)
206             _len, _, tail = len_decode(len_encoded)
207             self.assertSequenceEqual(tail, b"")
208             self.assertEqual(klass, eklass)
209             self.assertEqual(num, etag)
210             self.assertEqual(_len, elen)
211             self.assertEqual(form, eform)
212
213     def test_go_vectors_invalid(self):
214         for data in (
215                 b"\x00\x83\x01\x00",
216                 b"\x1f\x85",
217                 b"\x30\x80",
218                 b"\xa0\x82\x00\xff",
219                 b"\xa0\x81\x7f",
220         ):
221             with self.assertRaises(DecodeError):
222                 _, _, len_encoded = tag_strip(memoryview(data))
223                 len_decode(len_encoded)
224
225     @given(
226         integers(min_value=0, max_value=127),
227         integers(min_value=0, max_value=2),
228     )
229     def test_long_instead_of_short(self, l, dummy_num):
230         octets = (b"\x00" * dummy_num) + int2byte(l)
231         octets = int2byte((dummy_num + 1) | 0x80) + octets
232         with self.assertRaises(DecodeError):
233             len_decode(octets)
234
235
236 class TestLenCoder(TestCase):
237     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
238     @given(
239         integers(min_value=0, max_value=127),
240         binary(max_size=5),
241     )
242     def test_short(self, l, junk):
243         raw = len_encode(l) + junk
244         decoded, llen, tail = len_decode(memoryview(raw))
245         self.assertEqual(decoded, l)
246         self.assertEqual(llen, 1)
247         self.assertEqual(len(raw), 1 + len(junk))
248         self.assertEqual(tail.tobytes(), junk)
249
250     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
251     @given(
252         integers(min_value=128),
253         binary(max_size=5),
254     )
255     def test_long(self, l, junk):
256         raw = len_encode(l) + junk
257         decoded, llen, tail = len_decode(memoryview(raw))
258         self.assertEqual(decoded, l)
259         self.assertEqual((llen - 1) | 0x80, byte2int(raw))
260         self.assertEqual(llen, len(raw) - len(junk))
261         self.assertNotEqual(indexbytes(raw, 1), 0)
262         self.assertSequenceEqual(tail.tobytes(), junk)
263
264     def test_empty(self):
265         with self.assertRaises(NotEnoughData):
266             len_decode(b"")
267
268     @given(integers(min_value=128))
269     def test_stripped(self, _len):
270         with self.assertRaises(NotEnoughData):
271             len_decode(len_encode(_len)[:-1])
272
273
274 text_printable = text(alphabet=printable, min_size=1)
275
276
277 @composite
278 def text_letters(draw):
279     result = draw(text(alphabet=ascii_letters, min_size=1))
280     if PY2:
281         result = result.encode("ascii")
282     return result
283
284
285 class CommonMixin(object):
286     def test_tag_default(self):
287         obj = self.base_klass()
288         self.assertEqual(obj.tag, obj.tag_default)
289
290     def test_simultaneous_impl_expl(self):
291         with self.assertRaises(ValueError):
292             self.base_klass(impl=b"whatever", expl=b"whenever")
293
294     @given(binary(min_size=1), integers(), integers(), integers())
295     def test_decoded(self, impl, offset, llen, vlen):
296         obj = self.base_klass(impl=impl, _decoded=(offset, llen, vlen))
297         self.assertEqual(obj.offset, offset)
298         self.assertEqual(obj.llen, llen)
299         self.assertEqual(obj.vlen, vlen)
300         self.assertEqual(obj.tlen, len(impl))
301         self.assertEqual(obj.tlvlen, obj.tlen + obj.llen + obj.vlen)
302
303     @given(binary(min_size=1))
304     def test_impl_inherited(self, impl_tag):
305         class Inherited(self.base_klass):
306             impl = impl_tag
307         obj = Inherited()
308         self.assertSequenceEqual(obj.impl, impl_tag)
309         self.assertFalse(obj.expled)
310
311     @given(binary())
312     def test_expl_inherited(self, expl_tag):
313         class Inherited(self.base_klass):
314             expl = expl_tag
315         obj = Inherited()
316         self.assertSequenceEqual(obj.expl, expl_tag)
317         self.assertTrue(obj.expled)
318
319     def assert_copied_basic_fields(self, obj, obj_copied):
320         self.assertEqual(obj, obj_copied)
321         self.assertSequenceEqual(obj.tag, obj_copied.tag)
322         self.assertEqual(obj.expl_tag, obj_copied.expl_tag)
323         self.assertEqual(obj.default, obj_copied.default)
324         self.assertEqual(obj.optional, obj_copied.optional)
325         self.assertEqual(obj.offset, obj_copied.offset)
326         self.assertEqual(obj.llen, obj_copied.llen)
327         self.assertEqual(obj.vlen, obj_copied.vlen)
328
329
330 @composite
331 def boolean_values_strategy(draw, do_expl=False):
332     value = draw(one_of(none(), booleans()))
333     impl = None
334     expl = None
335     if do_expl:
336         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
337     else:
338         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
339     default = draw(one_of(none(), booleans()))
340     optional = draw(one_of(none(), booleans()))
341     _decoded = (
342         draw(integers(min_value=0)),
343         draw(integers(min_value=0)),
344         draw(integers(min_value=0)),
345     )
346     return (value, impl, expl, default, optional, _decoded)
347
348
349 class BooleanInherited(Boolean):
350     pass
351
352
353 class TestBoolean(CommonMixin, TestCase):
354     base_klass = Boolean
355
356     def test_invalid_value_type(self):
357         with self.assertRaises(InvalidValueType) as err:
358             Boolean(123)
359         repr(err.exception)
360
361     @given(booleans())
362     def test_optional(self, optional):
363         obj = Boolean(default=Boolean(False), optional=optional)
364         self.assertTrue(obj.optional)
365
366     @given(booleans())
367     def test_ready(self, value):
368         obj = Boolean()
369         self.assertFalse(obj.ready)
370         repr(obj)
371         pprint(obj)
372         with self.assertRaises(ObjNotReady) as err:
373             obj.encode()
374         repr(err.exception)
375         obj = Boolean(value)
376         self.assertTrue(obj.ready)
377         repr(obj)
378         pprint(obj)
379
380     @given(booleans(), booleans(), binary(), binary())
381     def test_comparison(self, value1, value2, tag1, tag2):
382         for klass in (Boolean, BooleanInherited):
383             obj1 = klass(value1)
384             obj2 = klass(value2)
385             self.assertEqual(obj1 == obj2, value1 == value2)
386             self.assertEqual(obj1 != obj2, value1 != value2)
387             self.assertEqual(obj1 == bool(obj2), value1 == value2)
388             obj1 = klass(value1, impl=tag1)
389             obj2 = klass(value1, impl=tag2)
390             self.assertEqual(obj1 == obj2, tag1 == tag2)
391             self.assertEqual(obj1 != obj2, tag1 != tag2)
392
393     @given(data_strategy())
394     def test_call(self, d):
395         for klass in (Boolean, BooleanInherited):
396             (
397                 value_initial,
398                 impl_initial,
399                 expl_initial,
400                 default_initial,
401                 optional_initial,
402                 _decoded_initial,
403             ) = d.draw(boolean_values_strategy())
404             obj_initial = klass(
405                 value_initial,
406                 impl_initial,
407                 expl_initial,
408                 default_initial,
409                 optional_initial or False,
410                 _decoded_initial,
411             )
412             (
413                 value,
414                 impl,
415                 expl,
416                 default,
417                 optional,
418                 _decoded,
419             ) = d.draw(boolean_values_strategy(do_expl=impl_initial is None))
420             obj = obj_initial(value, impl, expl, default, optional)
421             if obj.ready:
422                 value_expected = default if value is None else value
423                 value_expected = (
424                     default_initial if value_expected is None
425                     else value_expected
426                 )
427                 self.assertEqual(obj, value_expected)
428             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
429             self.assertEqual(obj.expl_tag, expl or expl_initial)
430             self.assertEqual(
431                 obj.default,
432                 default_initial if default is None else default,
433             )
434             if obj.default is None:
435                 optional = optional_initial if optional is None else optional
436                 optional = False if optional is None else optional
437             else:
438                 optional = True
439             self.assertEqual(obj.optional, optional)
440
441     @given(boolean_values_strategy())
442     def test_copy(self, values):
443         for klass in (Boolean, BooleanInherited):
444             obj = klass(*values)
445             obj_copied = obj.copy()
446             self.assert_copied_basic_fields(obj, obj_copied)
447
448     @given(
449         booleans(),
450         integers(min_value=1).map(tag_encode),
451     )
452     def test_stripped(self, value, tag_impl):
453         obj = Boolean(value, impl=tag_impl)
454         with self.assertRaises(NotEnoughData):
455             obj.decode(obj.encode()[:-1])
456
457     @given(
458         booleans(),
459         integers(min_value=1).map(tag_ctxc),
460     )
461     def test_stripped_expl(self, value, tag_expl):
462         obj = Boolean(value, expl=tag_expl)
463         with self.assertRaises(NotEnoughData):
464             obj.decode(obj.encode()[:-1])
465
466     @given(
467         integers(min_value=31),
468         integers(min_value=0),
469         decode_path_strat,
470     )
471     def test_bad_tag(self, tag, offset, decode_path):
472         with self.assertRaises(DecodeError) as err:
473             Boolean().decode(
474                 tag_encode(tag)[:-1],
475                 offset=offset,
476                 decode_path=decode_path,
477             )
478         repr(err.exception)
479         self.assertEqual(err.exception.offset, offset)
480         self.assertEqual(err.exception.decode_path, decode_path)
481
482     @given(
483         integers(min_value=31),
484         integers(min_value=0),
485         decode_path_strat,
486     )
487     def test_bad_expl_tag(self, tag, offset, decode_path):
488         with self.assertRaises(DecodeError) as err:
489             Boolean(expl=Boolean.tag_default).decode(
490                 tag_encode(tag)[:-1],
491                 offset=offset,
492                 decode_path=decode_path,
493             )
494         repr(err.exception)
495         self.assertEqual(err.exception.offset, offset)
496         self.assertEqual(err.exception.decode_path, decode_path)
497
498     @given(
499         integers(min_value=128),
500         integers(min_value=0),
501         decode_path_strat,
502     )
503     def test_bad_len(self, l, offset, decode_path):
504         with self.assertRaises(DecodeError) as err:
505             Boolean().decode(
506                 Boolean.tag_default + len_encode(l)[:-1],
507                 offset=offset,
508                 decode_path=decode_path,
509             )
510         repr(err.exception)
511         self.assertEqual(err.exception.offset, offset)
512         self.assertEqual(err.exception.decode_path, decode_path)
513
514     @given(
515         integers(min_value=128),
516         integers(min_value=0),
517         decode_path_strat,
518     )
519     def test_bad_expl_len(self, l, offset, decode_path):
520         with self.assertRaises(DecodeError) as err:
521             Boolean(expl=Boolean.tag_default).decode(
522                 Boolean.tag_default + len_encode(l)[:-1],
523                 offset=offset,
524                 decode_path=decode_path,
525             )
526         repr(err.exception)
527         self.assertEqual(err.exception.offset, offset)
528         self.assertEqual(err.exception.decode_path, decode_path)
529
530     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
531     @given(
532         boolean_values_strategy(),
533         booleans(),
534         integers(min_value=1).map(tag_ctxc),
535         integers(min_value=0),
536         binary(max_size=5),
537     )
538     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
539         for klass in (Boolean, BooleanInherited):
540             _, _, _, default, optional, _decoded = values
541             obj = klass(
542                 value=value,
543                 default=default,
544                 optional=optional,
545                 _decoded=_decoded,
546             )
547             repr(obj)
548             pprint(obj)
549             self.assertFalse(obj.expled)
550             obj_encoded = obj.encode()
551             obj_expled = obj(value, expl=tag_expl)
552             self.assertTrue(obj_expled.expled)
553             repr(obj_expled)
554             pprint(obj_expled)
555             obj_expled_encoded = obj_expled.encode()
556             obj_decoded, tail = obj_expled.decode(
557                 obj_expled_encoded + tail_junk,
558                 offset=offset,
559             )
560             repr(obj_decoded)
561             pprint(obj_decoded)
562             self.assertEqual(tail, tail_junk)
563             self.assertEqual(obj_decoded, obj_expled)
564             self.assertNotEqual(obj_decoded, obj)
565             self.assertEqual(bool(obj_decoded), bool(obj_expled))
566             self.assertEqual(bool(obj_decoded), bool(obj))
567             self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
568             self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
569             self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
570             self.assertEqual(
571                 obj_decoded.expl_llen,
572                 len(len_encode(len(obj_encoded))),
573             )
574             self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
575             self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
576             self.assertEqual(
577                 obj_decoded.offset,
578                 offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
579             )
580             self.assertEqual(obj_decoded.expl_offset, offset)
581
582     @given(integers(min_value=2))
583     def test_invalid_len(self, l):
584         with self.assertRaises(InvalidLength):
585             Boolean().decode(b"".join((
586                 Boolean.tag_default,
587                 len_encode(l),
588                 b"\x00" * l,
589             )))
590
591     @given(integers(min_value=0 + 1, max_value=255 - 1))
592     def test_ber_value(self, value):
593         with assertRaisesRegex(self, DecodeError, "unacceptable Boolean value"):
594             Boolean().decode(b"".join((
595                 Boolean.tag_default,
596                 len_encode(1),
597                 int2byte(value),
598             )))
599         obj, _ = Boolean().decode(
600             b"".join((
601                 Boolean.tag_default,
602                 len_encode(1),
603                 int2byte(value),
604             )),
605             ctx={"bered": True},
606         )
607         self.assertTrue(bool(obj))
608         self.assertTrue(obj.bered)
609         self.assertFalse(obj.lenindef)
610
611     @given(
612         integers(min_value=1).map(tag_ctxc),
613         binary().filter(lambda x: not x.startswith(EOC)),
614     )
615     def test_ber_expl_no_eoc(self, expl, junk):
616         encoded = expl + b"\x80" + 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                 b"\x80" +
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                 b"\x80" +
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             b"\x80" +
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) + b"\x80" + 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) + b"\x80" + 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                     b"\x80" +
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                 b"\x80" +
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             b"\x80" +
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) + b"\x80" + 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             b"\x80" +
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_encode(tag) for tag in draw(sets(
3932             integers(min_value=0),
3933             min_size=len(names),
3934             max_size=len(names),
3935         ))]
3936         schema = [(name, Integer(impl=tag)) for name, tag in zip(names, tags)]
3937     value = None
3938     if value_required or draw(booleans()):
3939         value = draw(tuples(
3940             sampled_from([name for name, _ in schema]),
3941             integers().map(Integer),
3942         ))
3943     expl = None
3944     if do_expl:
3945         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
3946     default = draw(one_of(
3947         none(),
3948         tuples(sampled_from([name for name, _ in schema]), integers().map(Integer)),
3949     ))
3950     optional = draw(one_of(none(), booleans()))
3951     _decoded = (
3952         draw(integers(min_value=0)),
3953         draw(integers(min_value=0)),
3954         draw(integers(min_value=0)),
3955     )
3956     return (schema, value, expl, default, optional, _decoded)
3957
3958
3959 class ChoiceInherited(Choice):
3960     pass
3961
3962
3963 class TestChoice(CommonMixin, TestCase):
3964     class Wahl(Choice):
3965         schema = (("whatever", Boolean()),)
3966     base_klass = Wahl
3967
3968     def test_schema_required(self):
3969         with assertRaisesRegex(self, ValueError, "schema must be specified"):
3970             Choice()
3971
3972     def test_impl_forbidden(self):
3973         with assertRaisesRegex(self, ValueError, "no implicit tag allowed"):
3974             Choice(impl=b"whatever")
3975
3976     def test_invalid_value_type(self):
3977         with self.assertRaises(InvalidValueType) as err:
3978             self.base_klass(123)
3979         repr(err.exception)
3980         with self.assertRaises(ObjUnknown) as err:
3981             self.base_klass(("whenever", Boolean(False)))
3982         repr(err.exception)
3983         with self.assertRaises(InvalidValueType) as err:
3984             self.base_klass(("whatever", Integer(123)))
3985         repr(err.exception)
3986
3987     @given(booleans())
3988     def test_optional(self, optional):
3989         obj = self.base_klass(
3990             default=self.base_klass(("whatever", Boolean(False))),
3991             optional=optional,
3992         )
3993         self.assertTrue(obj.optional)
3994
3995     @given(booleans())
3996     def test_ready(self, value):
3997         obj = self.base_klass()
3998         self.assertFalse(obj.ready)
3999         repr(obj)
4000         pprint(obj)
4001         self.assertIsNone(obj["whatever"])
4002         with self.assertRaises(ObjNotReady) as err:
4003             obj.encode()
4004         repr(err.exception)
4005         obj["whatever"] = Boolean()
4006         self.assertFalse(obj.ready)
4007         repr(obj)
4008         pprint(obj)
4009         obj["whatever"] = Boolean(value)
4010         self.assertTrue(obj.ready)
4011         repr(obj)
4012         pprint(obj)
4013
4014     @given(booleans(), booleans())
4015     def test_comparison(self, value1, value2):
4016         class WahlInherited(self.base_klass):
4017             pass
4018         for klass in (self.base_klass, WahlInherited):
4019             obj1 = klass(("whatever", Boolean(value1)))
4020             obj2 = klass(("whatever", Boolean(value2)))
4021             self.assertEqual(obj1 == obj2, value1 == value2)
4022             self.assertEqual(obj1 != obj2, value1 != value2)
4023             self.assertEqual(obj1 == obj2._value, value1 == value2)
4024             self.assertFalse(obj1 == obj2._value[1])
4025
4026     @given(data_strategy())
4027     def test_call(self, d):
4028         for klass in (Choice, ChoiceInherited):
4029             (
4030                 schema_initial,
4031                 value_initial,
4032                 expl_initial,
4033                 default_initial,
4034                 optional_initial,
4035                 _decoded_initial,
4036             ) = d.draw(choice_values_strategy())
4037
4038             class Wahl(klass):
4039                 schema = schema_initial
4040             obj_initial = Wahl(
4041                 value=value_initial,
4042                 expl=expl_initial,
4043                 default=default_initial,
4044                 optional=optional_initial or False,
4045                 _decoded=_decoded_initial,
4046             )
4047             (
4048                 _,
4049                 value,
4050                 expl,
4051                 default,
4052                 optional,
4053                 _decoded,
4054             ) = d.draw(choice_values_strategy(schema=schema_initial, do_expl=True))
4055             obj = obj_initial(value, expl, default, optional)
4056             if obj.ready:
4057                 value_expected = default if value is None else value
4058                 value_expected = (
4059                     default_initial if value_expected is None
4060                     else value_expected
4061                 )
4062                 self.assertEqual(obj.choice, value_expected[0])
4063                 self.assertEqual(obj.value, int(value_expected[1]))
4064             self.assertEqual(obj.expl_tag, expl or expl_initial)
4065             default_expect = default_initial if default is None else default
4066             if default_expect is not None:
4067                 self.assertEqual(obj.default.choice, default_expect[0])
4068                 self.assertEqual(obj.default.value, int(default_expect[1]))
4069             if obj.default is None:
4070                 optional = optional_initial if optional is None else optional
4071                 optional = False if optional is None else optional
4072             else:
4073                 optional = True
4074             self.assertEqual(obj.optional, optional)
4075             self.assertEqual(obj.specs, obj_initial.specs)
4076
4077     def test_simultaneous_impl_expl(self):
4078         # override it, as Any does not have implicit tag
4079         pass
4080
4081     def test_decoded(self):
4082         # override it, as Any does not have implicit tag
4083         pass
4084
4085     @given(choice_values_strategy())
4086     def test_copy(self, values):
4087         _schema, value, expl, default, optional, _decoded = values
4088
4089         class Wahl(self.base_klass):
4090             schema = _schema
4091         obj = Wahl(
4092             value=value,
4093             expl=expl,
4094             default=default,
4095             optional=optional or False,
4096             _decoded=_decoded,
4097         )
4098         obj_copied = obj.copy()
4099         self.assertIsNone(obj.tag)
4100         self.assertIsNone(obj_copied.tag)
4101         # hack for assert_copied_basic_fields
4102         obj.tag = "whatever"
4103         obj_copied.tag = "whatever"
4104         self.assert_copied_basic_fields(obj, obj_copied)
4105         self.assertEqual(obj._value, obj_copied._value)
4106         self.assertEqual(obj.specs, obj_copied.specs)
4107
4108     @given(booleans())
4109     def test_stripped(self, value):
4110         obj = self.base_klass(("whatever", Boolean(value)))
4111         with self.assertRaises(NotEnoughData):
4112             obj.decode(obj.encode()[:-1])
4113
4114     @given(
4115         booleans(),
4116         integers(min_value=1).map(tag_ctxc),
4117     )
4118     def test_stripped_expl(self, value, tag_expl):
4119         obj = self.base_klass(("whatever", Boolean(value)), expl=tag_expl)
4120         with self.assertRaises(NotEnoughData):
4121             obj.decode(obj.encode()[:-1])
4122
4123     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4124     @given(data_strategy())
4125     def test_symmetric(self, d):
4126         _schema, value, _, default, optional, _decoded = d.draw(
4127             choice_values_strategy(value_required=True)
4128         )
4129         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4130         offset = d.draw(integers(min_value=0))
4131         tail_junk = d.draw(binary(max_size=5))
4132
4133         class Wahl(self.base_klass):
4134             schema = _schema
4135         obj = Wahl(
4136             value=value,
4137             default=default,
4138             optional=optional,
4139             _decoded=_decoded,
4140         )
4141         repr(obj)
4142         pprint(obj)
4143         self.assertFalse(obj.expled)
4144         obj_encoded = obj.encode()
4145         obj_expled = obj(value, expl=tag_expl)
4146         self.assertTrue(obj_expled.expled)
4147         repr(obj_expled)
4148         pprint(obj_expled)
4149         obj_expled_encoded = obj_expled.encode()
4150         obj_decoded, tail = obj_expled.decode(
4151             obj_expled_encoded + tail_junk,
4152             offset=offset,
4153         )
4154         repr(obj_decoded)
4155         pprint(obj_decoded)
4156         self.assertEqual(tail, tail_junk)
4157         self.assertEqual(obj_decoded, obj_expled)
4158         self.assertEqual(obj_decoded.choice, obj_expled.choice)
4159         self.assertEqual(obj_decoded.value, obj_expled.value)
4160         self.assertEqual(obj_decoded.choice, obj.choice)
4161         self.assertEqual(obj_decoded.value, obj.value)
4162         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
4163         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
4164         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
4165         self.assertEqual(
4166             obj_decoded.expl_llen,
4167             len(len_encode(len(obj_encoded))),
4168         )
4169         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
4170         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
4171         self.assertEqual(
4172             obj_decoded.offset,
4173             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
4174         )
4175         self.assertEqual(obj_decoded.expl_offset, offset)
4176         self.assertSequenceEqual(
4177             obj_expled_encoded[
4178                 obj_decoded.value.offset - offset:
4179                 obj_decoded.value.offset + obj_decoded.value.tlvlen - offset
4180             ],
4181             obj_encoded,
4182         )
4183
4184     @given(integers())
4185     def test_set_get(self, value):
4186         class Wahl(Choice):
4187             schema = (
4188                 ("erste", Boolean()),
4189                 ("zweite", Integer()),
4190             )
4191         obj = Wahl()
4192         with self.assertRaises(ObjUnknown) as err:
4193             obj["whatever"] = "whenever"
4194         with self.assertRaises(InvalidValueType) as err:
4195             obj["zweite"] = Boolean(False)
4196         obj["zweite"] = Integer(value)
4197         repr(err.exception)
4198         with self.assertRaises(ObjUnknown) as err:
4199             obj["whatever"]
4200         repr(err.exception)
4201         self.assertIsNone(obj["erste"])
4202         self.assertEqual(obj["zweite"], Integer(value))
4203
4204     def test_tag_mismatch(self):
4205         class Wahl(Choice):
4206             schema = (
4207                 ("erste", Boolean()),
4208             )
4209         int_encoded = Integer(123).encode()
4210         bool_encoded = Boolean(False).encode()
4211         obj = Wahl()
4212         obj.decode(bool_encoded)
4213         with self.assertRaises(TagMismatch):
4214             obj.decode(int_encoded)
4215
4216     def test_tag_mismatch_underlying(self):
4217         class SeqOfBoolean(SequenceOf):
4218             schema = Boolean()
4219
4220         class SeqOfInteger(SequenceOf):
4221             schema = Integer()
4222
4223         class Wahl(Choice):
4224             schema = (
4225                 ("erste", SeqOfBoolean()),
4226             )
4227
4228         int_encoded = SeqOfInteger((Integer(123),)).encode()
4229         bool_encoded = SeqOfBoolean((Boolean(False),)).encode()
4230         obj = Wahl()
4231         obj.decode(bool_encoded)
4232         with self.assertRaises(TagMismatch) as err:
4233             obj.decode(int_encoded)
4234         self.assertEqual(err.exception.decode_path, ("erste", "0"))
4235
4236
4237 @composite
4238 def seq_values_strategy(draw, seq_klass, do_expl=False):
4239     value = None
4240     if draw(booleans()):
4241         value = seq_klass()
4242         value._value = {
4243             k: v for k, v in draw(dictionaries(
4244                 integers(),
4245                 one_of(
4246                     booleans().map(Boolean),
4247                     integers().map(Integer),
4248                 ),
4249             )).items()
4250         }
4251     schema = None
4252     if draw(booleans()):
4253         schema = list(draw(dictionaries(
4254             integers(),
4255             one_of(
4256                 booleans().map(Boolean),
4257                 integers().map(Integer),
4258             ),
4259         )).items())
4260     impl = None
4261     expl = None
4262     if do_expl:
4263         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4264     else:
4265         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4266     default = None
4267     if draw(booleans()):
4268         default = seq_klass()
4269         default._value = {
4270             k: v for k, v in draw(dictionaries(
4271                 integers(),
4272                 one_of(
4273                     booleans().map(Boolean),
4274                     integers().map(Integer),
4275                 ),
4276             )).items()
4277         }
4278     optional = draw(one_of(none(), booleans()))
4279     _decoded = (
4280         draw(integers(min_value=0)),
4281         draw(integers(min_value=0)),
4282         draw(integers(min_value=0)),
4283     )
4284     return (value, schema, impl, expl, default, optional, _decoded)
4285
4286
4287 @composite
4288 def sequence_strategy(draw, seq_klass):
4289     inputs = draw(lists(
4290         one_of(
4291             tuples(just(Boolean), booleans(), one_of(none(), booleans())),
4292             tuples(just(Integer), integers(), one_of(none(), integers())),
4293         ),
4294         max_size=6,
4295     ))
4296     tags = draw(sets(
4297         integers(min_value=1),
4298         min_size=len(inputs),
4299         max_size=len(inputs),
4300     ))
4301     inits = [
4302         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4303         for tag, expled in zip(tags, draw(lists(
4304             booleans(),
4305             min_size=len(inputs),
4306             max_size=len(inputs),
4307         )))
4308     ]
4309     empties = []
4310     for i, optional in enumerate(draw(lists(
4311             sampled_from(("required", "optional", "empty")),
4312             min_size=len(inputs),
4313             max_size=len(inputs),
4314     ))):
4315         if optional in ("optional", "empty"):
4316             inits[i]["optional"] = True
4317         if optional == "empty":
4318             empties.append(i)
4319     empties = set(empties)
4320     names = list(draw(sets(
4321         text_printable,
4322         min_size=len(inputs),
4323         max_size=len(inputs),
4324     )))
4325     schema = []
4326     for i, (klass, value, default) in enumerate(inputs):
4327         schema.append((names[i], klass(default=default, **inits[i])))
4328     seq_name = draw(text_letters())
4329     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4330     seq = Seq()
4331     expects = []
4332     for i, (klass, value, default) in enumerate(inputs):
4333         name = names[i]
4334         _, spec = schema[i]
4335         expect = {
4336             "name": name,
4337             "optional": False,
4338             "presented": False,
4339             "default_value": None if spec.default is None else default,
4340             "value": None,
4341         }
4342         if i in empties:
4343             expect["optional"] = True
4344         else:
4345             expect["presented"] = True
4346             expect["value"] = value
4347             if spec.optional:
4348                 expect["optional"] = True
4349             if default is not None and default == value:
4350                 expect["presented"] = False
4351             seq[name] = klass(value)
4352         expects.append(expect)
4353     return seq, expects
4354
4355
4356 @composite
4357 def sequences_strategy(draw, seq_klass):
4358     tags = draw(sets(integers(min_value=1), min_size=0, max_size=5))
4359     inits = [
4360         ({"expl": tag_ctxc(tag)} if expled else {"impl": tag_encode(tag)})
4361         for tag, expled in zip(tags, draw(lists(
4362             booleans(),
4363             min_size=len(tags),
4364             max_size=len(tags),
4365         )))
4366     ]
4367     defaulted = set(
4368         i for i, is_default in enumerate(draw(lists(
4369             booleans(),
4370             min_size=len(tags),
4371             max_size=len(tags),
4372         ))) if is_default
4373     )
4374     names = list(draw(sets(
4375         text_printable,
4376         min_size=len(tags),
4377         max_size=len(tags),
4378     )))
4379     seq_expectses = draw(lists(
4380         sequence_strategy(seq_klass=seq_klass),
4381         min_size=len(tags),
4382         max_size=len(tags),
4383     ))
4384     seqs = [seq for seq, _ in seq_expectses]
4385     schema = []
4386     for i, (name, seq) in enumerate(zip(names, seqs)):
4387         schema.append((
4388             name,
4389             seq(default=(seq if i in defaulted else None), **inits[i]),
4390         ))
4391     seq_name = draw(text_letters())
4392     Seq = type(seq_name, (seq_klass,), {"schema": tuple(schema)})
4393     seq_outer = Seq()
4394     expect_outers = []
4395     for name, (seq_inner, expects_inner) in zip(names, seq_expectses):
4396         expect = {
4397             "name": name,
4398             "expects": expects_inner,
4399             "presented": False,
4400         }
4401         seq_outer[name] = seq_inner
4402         if seq_outer.specs[name].default is None:
4403             expect["presented"] = True
4404         expect_outers.append(expect)
4405     return seq_outer, expect_outers
4406
4407
4408 class SeqMixing(object):
4409     def test_invalid_value_type(self):
4410         with self.assertRaises(InvalidValueType) as err:
4411             self.base_klass(123)
4412         repr(err.exception)
4413
4414     def test_invalid_value_type_set(self):
4415         class Seq(self.base_klass):
4416             schema = (("whatever", Boolean()),)
4417         seq = Seq()
4418         with self.assertRaises(InvalidValueType) as err:
4419             seq["whatever"] = Integer(123)
4420         repr(err.exception)
4421
4422     @given(booleans())
4423     def test_optional(self, optional):
4424         obj = self.base_klass(default=self.base_klass(), optional=optional)
4425         self.assertTrue(obj.optional)
4426
4427     @given(data_strategy())
4428     def test_ready(self, d):
4429         ready = {
4430             str(i): v for i, v in enumerate(d.draw(lists(
4431                 booleans(),
4432                 min_size=1,
4433                 max_size=3,
4434             )))
4435         }
4436         non_ready = {
4437             str(i + len(ready)): v for i, v in enumerate(d.draw(lists(
4438                 booleans(),
4439                 min_size=1,
4440                 max_size=3,
4441             )))
4442         }
4443         schema_input = []
4444         for name in d.draw(permutations(
4445                 list(ready.keys()) + list(non_ready.keys()),
4446         )):
4447             schema_input.append((name, Boolean()))
4448
4449         class Seq(self.base_klass):
4450             schema = tuple(schema_input)
4451         seq = Seq()
4452         for name in ready.keys():
4453             seq[name]
4454             seq[name] = Boolean()
4455         self.assertFalse(seq.ready)
4456         repr(seq)
4457         pprint(seq)
4458         for name, value in ready.items():
4459             seq[name] = Boolean(value)
4460         self.assertFalse(seq.ready)
4461         repr(seq)
4462         pprint(seq)
4463         with self.assertRaises(ObjNotReady) as err:
4464             seq.encode()
4465         repr(err.exception)
4466         for name, value in non_ready.items():
4467             seq[name] = Boolean(value)
4468         self.assertTrue(seq.ready)
4469         repr(seq)
4470         pprint(seq)
4471
4472     @given(data_strategy())
4473     def test_call(self, d):
4474         class SeqInherited(self.base_klass):
4475             pass
4476         for klass in (self.base_klass, SeqInherited):
4477             (
4478                 value_initial,
4479                 schema_initial,
4480                 impl_initial,
4481                 expl_initial,
4482                 default_initial,
4483                 optional_initial,
4484                 _decoded_initial,
4485             ) = d.draw(seq_values_strategy(seq_klass=klass))
4486             obj_initial = klass(
4487                 value_initial,
4488                 schema_initial,
4489                 impl_initial,
4490                 expl_initial,
4491                 default_initial,
4492                 optional_initial or False,
4493                 _decoded_initial,
4494             )
4495             (
4496                 value,
4497                 _,
4498                 impl,
4499                 expl,
4500                 default,
4501                 optional,
4502                 _decoded,
4503             ) = d.draw(seq_values_strategy(
4504                 seq_klass=klass,
4505                 do_expl=impl_initial is None,
4506             ))
4507             obj = obj_initial(value, impl, expl, default, optional)
4508             value_expected = default if value is None else value
4509             value_expected = (
4510                 default_initial if value_expected is None
4511                 else value_expected
4512             )
4513             self.assertEqual(obj._value, getattr(value_expected, "_value", {}))
4514             self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
4515             self.assertEqual(obj.expl_tag, expl or expl_initial)
4516             self.assertEqual(
4517                 {} if obj.default is None else obj.default._value,
4518                 getattr(default_initial if default is None else default, "_value", {}),
4519             )
4520             if obj.default is None:
4521                 optional = optional_initial if optional is None else optional
4522                 optional = False if optional is None else optional
4523             else:
4524                 optional = True
4525             self.assertEqual(list(obj.specs.items()), schema_initial or [])
4526             self.assertEqual(obj.optional, optional)
4527
4528     @given(data_strategy())
4529     def test_copy(self, d):
4530         class SeqInherited(self.base_klass):
4531             pass
4532         for klass in (self.base_klass, SeqInherited):
4533             values = d.draw(seq_values_strategy(seq_klass=klass))
4534             obj = klass(*values)
4535             obj_copied = obj.copy()
4536             self.assert_copied_basic_fields(obj, obj_copied)
4537             self.assertEqual(obj.specs, obj_copied.specs)
4538             self.assertEqual(obj._value, obj_copied._value)
4539
4540     @given(data_strategy())
4541     def test_stripped(self, d):
4542         value = d.draw(integers())
4543         tag_impl = tag_encode(d.draw(integers(min_value=1)))
4544
4545         class Seq(self.base_klass):
4546             impl = tag_impl
4547             schema = (("whatever", Integer()),)
4548         seq = Seq()
4549         seq["whatever"] = Integer(value)
4550         with self.assertRaises(NotEnoughData):
4551             seq.decode(seq.encode()[:-1])
4552
4553     @given(data_strategy())
4554     def test_stripped_expl(self, d):
4555         value = d.draw(integers())
4556         tag_expl = tag_ctxc(d.draw(integers(min_value=1)))
4557
4558         class Seq(self.base_klass):
4559             expl = tag_expl
4560             schema = (("whatever", Integer()),)
4561         seq = Seq()
4562         seq["whatever"] = Integer(value)
4563         with self.assertRaises(NotEnoughData):
4564             seq.decode(seq.encode()[:-1])
4565
4566     @given(binary(min_size=2))
4567     def test_non_tag_mismatch_raised(self, junk):
4568         try:
4569             _, _, len_encoded = tag_strip(memoryview(junk))
4570             len_decode(len_encoded)
4571         except Exception:
4572             assume(True)
4573         else:
4574             assume(False)
4575
4576         class Seq(self.base_klass):
4577             schema = (
4578                 ("whatever", Integer()),
4579                 ("junk", Any()),
4580                 ("whenever", Integer()),
4581             )
4582         seq = Seq()
4583         seq["whatever"] = Integer(123)
4584         seq["junk"] = Any(junk)
4585         seq["whenever"] = Integer(123)
4586         with self.assertRaises(DecodeError):
4587             seq.decode(seq.encode())
4588
4589     @given(
4590         integers(min_value=31),
4591         integers(min_value=0),
4592         decode_path_strat,
4593     )
4594     def test_bad_tag(self, tag, offset, decode_path):
4595         with self.assertRaises(DecodeError) as err:
4596             self.base_klass().decode(
4597                 tag_encode(tag)[:-1],
4598                 offset=offset,
4599                 decode_path=decode_path,
4600             )
4601         repr(err.exception)
4602         self.assertEqual(err.exception.offset, offset)
4603         self.assertEqual(err.exception.decode_path, decode_path)
4604
4605     @given(
4606         integers(min_value=128),
4607         integers(min_value=0),
4608         decode_path_strat,
4609     )
4610     def test_bad_len(self, l, offset, decode_path):
4611         with self.assertRaises(DecodeError) as err:
4612             self.base_klass().decode(
4613                 self.base_klass.tag_default + len_encode(l)[:-1],
4614                 offset=offset,
4615                 decode_path=decode_path,
4616             )
4617         repr(err.exception)
4618         self.assertEqual(err.exception.offset, offset)
4619         self.assertEqual(err.exception.decode_path, decode_path)
4620
4621     def _assert_expects(self, seq, expects):
4622         for expect in expects:
4623             self.assertEqual(
4624                 seq.specs[expect["name"]].optional,
4625                 expect["optional"],
4626             )
4627             if expect["default_value"] is not None:
4628                 self.assertEqual(
4629                     seq.specs[expect["name"]].default,
4630                     expect["default_value"],
4631                 )
4632             if expect["presented"]:
4633                 self.assertIn(expect["name"], seq)
4634                 self.assertEqual(seq[expect["name"]], expect["value"])
4635
4636     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4637     @given(data_strategy())
4638     def test_symmetric(self, d):
4639         seq, expects = d.draw(sequence_strategy(seq_klass=self.base_klass))
4640         tail_junk = d.draw(binary(max_size=5))
4641         self.assertTrue(seq.ready)
4642         self.assertFalse(seq.decoded)
4643         self._assert_expects(seq, expects)
4644         repr(seq)
4645         pprint(seq)
4646         self.assertTrue(seq.ready)
4647         seq_encoded = seq.encode()
4648         seq_decoded, tail = seq.decode(seq_encoded + tail_junk)
4649         self.assertFalse(seq_decoded.lenindef)
4650
4651         t, _, lv = tag_strip(seq_encoded)
4652         _, _, v = len_decode(lv)
4653         seq_encoded_lenindef = t + b"\x80" + v + EOC
4654         seq_decoded_lenindef, tail_lenindef = seq.decode(
4655             seq_encoded_lenindef + tail_junk,
4656             ctx={"bered": True},
4657         )
4658         self.assertTrue(seq_decoded_lenindef.lenindef)
4659         with self.assertRaises(DecodeError):
4660             seq.decode(seq_encoded_lenindef[:-1], ctx={"bered": True})
4661         with self.assertRaises(DecodeError):
4662             seq.decode(seq_encoded_lenindef[:-2], ctx={"bered": True})
4663         repr(seq_decoded_lenindef)
4664         pprint(seq_decoded_lenindef)
4665         self.assertTrue(seq_decoded_lenindef.ready)
4666
4667         for decoded, decoded_tail, encoded in (
4668                 (seq_decoded, tail, seq_encoded),
4669                 (seq_decoded_lenindef, tail_lenindef, seq_encoded_lenindef),
4670         ):
4671             self.assertEqual(decoded_tail, tail_junk)
4672             self._assert_expects(decoded, expects)
4673             self.assertEqual(seq, decoded)
4674             self.assertEqual(decoded.encode(), seq_encoded)
4675             self.assertEqual(decoded.tlvlen, len(encoded))
4676             for expect in expects:
4677                 if not expect["presented"]:
4678                     self.assertNotIn(expect["name"], decoded)
4679                     continue
4680                 self.assertIn(expect["name"], decoded)
4681                 obj = decoded[expect["name"]]
4682                 self.assertTrue(obj.decoded)
4683                 offset = obj.expl_offset if obj.expled else obj.offset
4684                 tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4685                 self.assertSequenceEqual(
4686                     seq_encoded[offset:offset + tlvlen],
4687                     obj.encode(),
4688                 )
4689
4690     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4691     @given(data_strategy())
4692     def test_symmetric_with_seq(self, d):
4693         seq, expect_outers = d.draw(sequences_strategy(seq_klass=self.base_klass))
4694         self.assertTrue(seq.ready)
4695         seq_encoded = seq.encode()
4696         seq_decoded, tail = seq.decode(seq_encoded)
4697         self.assertEqual(tail, b"")
4698         self.assertTrue(seq.ready)
4699         self.assertEqual(seq, seq_decoded)
4700         self.assertEqual(seq_decoded.encode(), seq_encoded)
4701         for expect_outer in expect_outers:
4702             if not expect_outer["presented"]:
4703                 self.assertNotIn(expect_outer["name"], seq_decoded)
4704                 continue
4705             self.assertIn(expect_outer["name"], seq_decoded)
4706             obj = seq_decoded[expect_outer["name"]]
4707             self.assertTrue(obj.decoded)
4708             offset = obj.expl_offset if obj.expled else obj.offset
4709             tlvlen = obj.expl_tlvlen if obj.expled else obj.tlvlen
4710             self.assertSequenceEqual(
4711                 seq_encoded[offset:offset + tlvlen],
4712                 obj.encode(),
4713             )
4714             self._assert_expects(obj, expect_outer["expects"])
4715
4716     @given(data_strategy())
4717     def test_default_disappears(self, d):
4718         _schema = list(d.draw(dictionaries(
4719             text_letters(),
4720             sets(integers(), min_size=2, max_size=2),
4721             min_size=1,
4722         )).items())
4723
4724         class Seq(self.base_klass):
4725             schema = [
4726                 (n, Integer(default=d))
4727                 for n, (_, d) in _schema
4728             ]
4729         seq = Seq()
4730         for name, (value, _) in _schema:
4731             seq[name] = Integer(value)
4732         self.assertEqual(len(seq._value), len(_schema))
4733         empty_seq = b"".join((self.base_klass.tag_default, len_encode(0)))
4734         self.assertGreater(len(seq.encode()), len(empty_seq))
4735         for name, (_, default) in _schema:
4736             seq[name] = Integer(default)
4737         self.assertEqual(len(seq._value), 0)
4738         self.assertSequenceEqual(seq.encode(), empty_seq)
4739
4740     @given(data_strategy())
4741     def test_encoded_default_accepted(self, d):
4742         _schema = list(d.draw(dictionaries(
4743             text_letters(),
4744             integers(),
4745             min_size=1,
4746         )).items())
4747         tags = [tag_encode(tag) for tag in d.draw(sets(
4748             integers(min_value=0),
4749             min_size=len(_schema),
4750             max_size=len(_schema),
4751         ))]
4752
4753         class SeqWithoutDefault(self.base_klass):
4754             schema = [
4755                 (n, Integer(impl=t))
4756                 for (n, _), t in zip(_schema, tags)
4757             ]
4758         seq_without_default = SeqWithoutDefault()
4759         for name, value in _schema:
4760             seq_without_default[name] = Integer(value)
4761         seq_encoded = seq_without_default.encode()
4762
4763         class SeqWithDefault(self.base_klass):
4764             schema = [
4765                 (n, Integer(default=v, impl=t))
4766                 for (n, v), t in zip(_schema, tags)
4767             ]
4768         seq_with_default = SeqWithDefault()
4769         seq_decoded, _ = seq_with_default.decode(seq_encoded)
4770         for name, value in _schema:
4771             self.assertEqual(seq_decoded[name], seq_with_default[name])
4772             self.assertEqual(seq_decoded[name], value)
4773
4774     @given(data_strategy())
4775     def test_missing_from_spec(self, d):
4776         names = list(d.draw(sets(text_letters(), min_size=2)))
4777         tags = [tag_encode(tag) for tag in d.draw(sets(
4778             integers(min_value=0),
4779             min_size=len(names),
4780             max_size=len(names),
4781         ))]
4782         names_tags = [(name, tag) for tag, name in sorted(zip(tags, names))]
4783
4784         class SeqFull(self.base_klass):
4785             schema = [(n, Integer(impl=t)) for n, t in names_tags]
4786         seq_full = SeqFull()
4787         for i, name in enumerate(names):
4788             seq_full[name] = Integer(i)
4789         seq_encoded = seq_full.encode()
4790         altered = names_tags[:-2] + names_tags[-1:]
4791
4792         class SeqMissing(self.base_klass):
4793             schema = [(n, Integer(impl=t)) for n, t in altered]
4794         seq_missing = SeqMissing()
4795         with self.assertRaises(TagMismatch):
4796             seq_missing.decode(seq_encoded)
4797
4798
4799 class TestSequence(SeqMixing, CommonMixin, TestCase):
4800     base_klass = Sequence
4801
4802     @given(
4803         integers(),
4804         binary(min_size=1),
4805     )
4806     def test_remaining(self, value, junk):
4807         class Seq(Sequence):
4808             schema = (
4809                 ("whatever", Integer()),
4810             )
4811         int_encoded = Integer(value).encode()
4812         junked = b"".join((
4813             Sequence.tag_default,
4814             len_encode(len(int_encoded + junk)),
4815             int_encoded + junk,
4816         ))
4817         with assertRaisesRegex(self, DecodeError, "remaining"):
4818             Seq().decode(junked)
4819
4820     @given(sets(text_letters(), min_size=2))
4821     def test_obj_unknown(self, names):
4822         missing = names.pop()
4823
4824         class Seq(Sequence):
4825             schema = [(n, Boolean()) for n in names]
4826         seq = Seq()
4827         with self.assertRaises(ObjUnknown) as err:
4828             seq[missing]
4829         repr(err.exception)
4830         with self.assertRaises(ObjUnknown) as err:
4831             seq[missing] = Boolean()
4832         repr(err.exception)
4833
4834     def test_x690_vector(self):
4835         class Seq(Sequence):
4836             schema = (
4837                 ("name", IA5String()),
4838                 ("ok", Boolean()),
4839             )
4840         seq = Seq().decode(hexdec("300A1605536d6974680101FF"))[0]
4841         self.assertEqual(seq["name"], "Smith")
4842         self.assertEqual(seq["ok"], True)
4843
4844
4845 class TestSet(SeqMixing, CommonMixin, TestCase):
4846     base_klass = Set
4847
4848     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
4849     @given(data_strategy())
4850     def test_sorted(self, d):
4851         tags = [
4852             tag_encode(tag) for tag in
4853             d.draw(sets(integers(min_value=1), min_size=1, max_size=10))
4854         ]
4855
4856         class Seq(Set):
4857             schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
4858         seq = Seq()
4859         for name, _ in Seq.schema:
4860             seq[name] = OctetString(b"")
4861         seq_encoded = seq.encode()
4862         seq_decoded, _ = seq.decode(seq_encoded)
4863         self.assertSequenceEqual(
4864             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
4865             b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
4866         )
4867
4868
4869 @composite
4870 def seqof_values_strategy(draw, schema=None, do_expl=False):
4871     if schema is None:
4872         schema = draw(sampled_from((Boolean(), Integer())))
4873     bound_min, bound_max = sorted(draw(sets(
4874         integers(min_value=0, max_value=10),
4875         min_size=2,
4876         max_size=2,
4877     )))
4878     if isinstance(schema, Boolean):
4879         values_generator = booleans().map(Boolean)
4880     elif isinstance(schema, Integer):
4881         values_generator = integers().map(Integer)
4882     values_generator = lists(
4883         values_generator,
4884         min_size=bound_min,
4885         max_size=bound_max,
4886     )
4887     values = draw(one_of(none(), values_generator))
4888     impl = None
4889     expl = None
4890     if do_expl:
4891         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4892     else:
4893         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
4894     default = draw(one_of(none(), values_generator))
4895     optional = draw(one_of(none(), booleans()))
4896     _decoded = (
4897         draw(integers(min_value=0)),
4898         draw(integers(min_value=0)),
4899         draw(integers(min_value=0)),
4900     )
4901     return (
4902         schema,
4903         values,
4904         (bound_min, bound_max),
4905         impl,
4906         expl,
4907         default,
4908         optional,
4909         _decoded,
4910     )
4911
4912
4913 class SeqOfMixing(object):
4914     def test_invalid_value_type(self):
4915         with self.assertRaises(InvalidValueType) as err:
4916             self.base_klass(123)
4917         repr(err.exception)
4918
4919     def test_invalid_values_type(self):
4920         class SeqOf(self.base_klass):
4921             schema = Integer()
4922         with self.assertRaises(InvalidValueType) as err:
4923             SeqOf([Integer(123), Boolean(False), Integer(234)])
4924         repr(err.exception)
4925
4926     def test_schema_required(self):
4927         with assertRaisesRegex(self, ValueError, "schema must be specified"):
4928             self.base_klass.__mro__[1]()
4929
4930     @given(booleans(), booleans(), binary(), binary())
4931     def test_comparison(self, value1, value2, tag1, tag2):
4932         class SeqOf(self.base_klass):
4933             schema = Boolean()
4934         obj1 = SeqOf([Boolean(value1)])
4935         obj2 = SeqOf([Boolean(value2)])
4936         self.assertEqual(obj1 == obj2, value1 == value2)
4937         self.assertEqual(obj1 != obj2, value1 != value2)
4938         self.assertEqual(obj1 == list(obj2), value1 == value2)
4939         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
4940         obj1 = SeqOf([Boolean(value1)], impl=tag1)
4941         obj2 = SeqOf([Boolean(value1)], impl=tag2)
4942         self.assertEqual(obj1 == obj2, tag1 == tag2)
4943         self.assertEqual(obj1 != obj2, tag1 != tag2)
4944
4945     @given(lists(booleans()))
4946     def test_iter(self, values):
4947         class SeqOf(self.base_klass):
4948             schema = Boolean()
4949         obj = SeqOf([Boolean(value) for value in values])
4950         self.assertEqual(len(obj), len(values))
4951         for i, value in enumerate(obj):
4952             self.assertEqual(value, values[i])
4953
4954     @given(data_strategy())
4955     def test_ready(self, d):
4956         ready = [Integer(v) for v in d.draw(lists(
4957             integers(),
4958             min_size=1,
4959             max_size=3,
4960         ))]
4961         non_ready = [
4962             Integer() for _ in
4963             range(d.draw(integers(min_value=1, max_value=5)))
4964         ]
4965
4966         class SeqOf(self.base_klass):
4967             schema = Integer()
4968         values = d.draw(permutations(ready + non_ready))
4969         seqof = SeqOf()
4970         for value in values:
4971             seqof.append(value)
4972         self.assertFalse(seqof.ready)
4973         repr(seqof)
4974         pprint(seqof)
4975         with self.assertRaises(ObjNotReady) as err:
4976             seqof.encode()
4977         repr(err.exception)
4978         for i, value in enumerate(values):
4979             self.assertEqual(seqof[i], value)
4980             if not seqof[i].ready:
4981                 seqof[i] = Integer(i)
4982         self.assertTrue(seqof.ready)
4983         repr(seqof)
4984         pprint(seqof)
4985
4986     def test_spec_mismatch(self):
4987         class SeqOf(self.base_klass):
4988             schema = Integer()
4989         seqof = SeqOf()
4990         seqof.append(Integer(123))
4991         with self.assertRaises(ValueError):
4992             seqof.append(Boolean(False))
4993         with self.assertRaises(ValueError):
4994             seqof[0] = Boolean(False)
4995
4996     @given(data_strategy())
4997     def test_bounds_satisfied(self, d):
4998         class SeqOf(self.base_klass):
4999             schema = Boolean()
5000         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
5001         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5002         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
5003         SeqOf(value=value, bounds=(bound_min, bound_max))
5004
5005     @given(data_strategy())
5006     def test_bounds_unsatisfied(self, d):
5007         class SeqOf(self.base_klass):
5008             schema = Boolean()
5009         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
5010         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
5011         value = [Boolean()] * d.draw(integers(max_value=bound_min - 1))
5012         with self.assertRaises(BoundsError) as err:
5013             SeqOf(value=value, bounds=(bound_min, bound_max))
5014         repr(err.exception)
5015         value = [Boolean()] * d.draw(integers(
5016             min_value=bound_max + 1,
5017             max_value=bound_max + 10,
5018         ))
5019         with self.assertRaises(BoundsError) as err:
5020             SeqOf(value=value, bounds=(bound_min, bound_max))
5021         repr(err.exception)
5022
5023     @given(integers(min_value=1, max_value=10))
5024     def test_out_of_bounds(self, bound_max):
5025         class SeqOf(self.base_klass):
5026             schema = Integer()
5027             bounds = (0, bound_max)
5028         seqof = SeqOf()
5029         for _ in range(bound_max):
5030             seqof.append(Integer(123))
5031         with self.assertRaises(BoundsError):
5032             seqof.append(Integer(123))
5033
5034     @given(data_strategy())
5035     def test_call(self, d):
5036         (
5037             schema_initial,
5038             value_initial,
5039             bounds_initial,
5040             impl_initial,
5041             expl_initial,
5042             default_initial,
5043             optional_initial,
5044             _decoded_initial,
5045         ) = d.draw(seqof_values_strategy())
5046
5047         class SeqOf(self.base_klass):
5048             schema = schema_initial
5049         obj_initial = SeqOf(
5050             value=value_initial,
5051             bounds=bounds_initial,
5052             impl=impl_initial,
5053             expl=expl_initial,
5054             default=default_initial,
5055             optional=optional_initial or False,
5056             _decoded=_decoded_initial,
5057         )
5058         (
5059             _,
5060             value,
5061             bounds,
5062             impl,
5063             expl,
5064             default,
5065             optional,
5066             _decoded,
5067         ) = d.draw(seqof_values_strategy(
5068             schema=schema_initial,
5069             do_expl=impl_initial is None,
5070         ))
5071         if (default is None) and (obj_initial.default is not None):
5072             bounds = None
5073         if (
5074                 (bounds is None) and
5075                 (value is not None) and
5076                 (bounds_initial is not None) and
5077                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
5078         ):
5079             value = None
5080         if (
5081                 (bounds is None) and
5082                 (default is not None) and
5083                 (bounds_initial is not None) and
5084                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
5085         ):
5086             default = None
5087         obj = obj_initial(
5088             value=value,
5089             bounds=bounds,
5090             impl=impl,
5091             expl=expl,
5092             default=default,
5093             optional=optional,
5094         )
5095         if obj.ready:
5096             value_expected = default if value is None else value
5097             value_expected = (
5098                 default_initial if value_expected is None
5099                 else value_expected
5100             )
5101             value_expected = () if value_expected is None else value_expected
5102             self.assertEqual(obj, value_expected)
5103         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
5104         self.assertEqual(obj.expl_tag, expl or expl_initial)
5105         self.assertEqual(
5106             obj.default,
5107             default_initial if default is None else default,
5108         )
5109         if obj.default is None:
5110             optional = optional_initial if optional is None else optional
5111             optional = False if optional is None else optional
5112         else:
5113             optional = True
5114         self.assertEqual(obj.optional, optional)
5115         self.assertEqual(
5116             (obj._bound_min, obj._bound_max),
5117             bounds or bounds_initial or (0, float("+inf")),
5118         )
5119
5120     @given(seqof_values_strategy())
5121     def test_copy(self, values):
5122         _schema, value, bounds, impl, expl, default, optional, _decoded = values
5123
5124         class SeqOf(self.base_klass):
5125             schema = _schema
5126         obj = SeqOf(
5127             value=value,
5128             bounds=bounds,
5129             impl=impl,
5130             expl=expl,
5131             default=default,
5132             optional=optional or False,
5133             _decoded=_decoded,
5134         )
5135         obj_copied = obj.copy()
5136         self.assert_copied_basic_fields(obj, obj_copied)
5137         self.assertEqual(obj._bound_min, obj_copied._bound_min)
5138         self.assertEqual(obj._bound_max, obj_copied._bound_max)
5139         self.assertEqual(obj._value, obj_copied._value)
5140
5141     @given(
5142         lists(binary()),
5143         integers(min_value=1).map(tag_encode),
5144     )
5145     def test_stripped(self, values, tag_impl):
5146         class SeqOf(self.base_klass):
5147             schema = OctetString()
5148         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
5149         with self.assertRaises(NotEnoughData):
5150             obj.decode(obj.encode()[:-1])
5151
5152     @given(
5153         lists(binary()),
5154         integers(min_value=1).map(tag_ctxc),
5155     )
5156     def test_stripped_expl(self, values, tag_expl):
5157         class SeqOf(self.base_klass):
5158             schema = OctetString()
5159         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
5160         with self.assertRaises(NotEnoughData):
5161             obj.decode(obj.encode()[:-1])
5162
5163     @given(
5164         integers(min_value=31),
5165         integers(min_value=0),
5166         decode_path_strat,
5167     )
5168     def test_bad_tag(self, tag, offset, decode_path):
5169         with self.assertRaises(DecodeError) as err:
5170             self.base_klass().decode(
5171                 tag_encode(tag)[:-1],
5172                 offset=offset,
5173                 decode_path=decode_path,
5174             )
5175         repr(err.exception)
5176         self.assertEqual(err.exception.offset, offset)
5177         self.assertEqual(err.exception.decode_path, decode_path)
5178
5179     @given(
5180         integers(min_value=128),
5181         integers(min_value=0),
5182         decode_path_strat,
5183     )
5184     def test_bad_len(self, l, offset, decode_path):
5185         with self.assertRaises(DecodeError) as err:
5186             self.base_klass().decode(
5187                 self.base_klass.tag_default + len_encode(l)[:-1],
5188                 offset=offset,
5189                 decode_path=decode_path,
5190             )
5191         repr(err.exception)
5192         self.assertEqual(err.exception.offset, offset)
5193         self.assertEqual(err.exception.decode_path, decode_path)
5194
5195     @given(binary(min_size=1))
5196     def test_tag_mismatch(self, impl):
5197         assume(impl != self.base_klass.tag_default)
5198         with self.assertRaises(TagMismatch):
5199             self.base_klass(impl=impl).decode(self.base_klass().encode())
5200
5201     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5202     @given(
5203         seqof_values_strategy(schema=Integer()),
5204         lists(integers().map(Integer)),
5205         integers(min_value=1).map(tag_ctxc),
5206         integers(min_value=0),
5207         binary(max_size=5),
5208     )
5209     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
5210         _, _, _, _, _, default, optional, _decoded = values
5211
5212         class SeqOf(self.base_klass):
5213             schema = Integer()
5214         obj = SeqOf(
5215             value=value,
5216             default=default,
5217             optional=optional,
5218             _decoded=_decoded,
5219         )
5220         repr(obj)
5221         pprint(obj)
5222         self.assertFalse(obj.expled)
5223         obj_encoded = obj.encode()
5224         obj_expled = obj(value, expl=tag_expl)
5225         self.assertTrue(obj_expled.expled)
5226         repr(obj_expled)
5227         pprint(obj_expled)
5228         obj_expled_encoded = obj_expled.encode()
5229         obj_decoded, tail = obj_expled.decode(
5230             obj_expled_encoded + tail_junk,
5231             offset=offset,
5232         )
5233         repr(obj_decoded)
5234         pprint(obj_decoded)
5235         self.assertEqual(tail, tail_junk)
5236         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
5237         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
5238         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
5239         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
5240         self.assertEqual(
5241             obj_decoded.expl_llen,
5242             len(len_encode(len(obj_encoded))),
5243         )
5244         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
5245         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
5246         self.assertEqual(
5247             obj_decoded.offset,
5248             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
5249         )
5250         self.assertEqual(obj_decoded.expl_offset, offset)
5251         for obj_inner in obj_decoded:
5252             self.assertIn(obj_inner, obj_decoded)
5253             self.assertSequenceEqual(
5254                 obj_inner.encode(),
5255                 obj_expled_encoded[
5256                     obj_inner.offset - offset:
5257                     obj_inner.offset + obj_inner.tlvlen - offset
5258                 ],
5259             )
5260
5261         t, _, lv = tag_strip(obj_encoded)
5262         _, _, v = len_decode(lv)
5263         obj_encoded_lenindef = t + b"\x80" + v + EOC
5264         obj_decoded_lenindef, tail_lenindef = obj.decode(
5265             obj_encoded_lenindef + tail_junk,
5266             ctx={"bered": True},
5267         )
5268         self.assertTrue(obj_decoded_lenindef.lenindef)
5269         repr(obj_decoded_lenindef)
5270         pprint(obj_decoded_lenindef)
5271         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
5272         with self.assertRaises(DecodeError):
5273             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
5274         with self.assertRaises(DecodeError):
5275             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
5276
5277
5278 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
5279     class SeqOf(SequenceOf):
5280         schema = "whatever"
5281     base_klass = SeqOf
5282
5283     def _test_symmetric_compare_objs(self, obj1, obj2):
5284         self.assertEqual(obj1, obj2)
5285         self.assertSequenceEqual(list(obj1), list(obj2))
5286
5287
5288 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
5289     class SeqOf(SetOf):
5290         schema = "whatever"
5291     base_klass = SeqOf
5292
5293     def _test_symmetric_compare_objs(self, obj1, obj2):
5294         self.assertSetEqual(
5295             set(int(v) for v in obj1),
5296             set(int(v) for v in obj2),
5297         )
5298
5299     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
5300     @given(data_strategy())
5301     def test_sorted(self, d):
5302         values = [OctetString(v) for v in d.draw(lists(binary()))]
5303
5304         class Seq(SetOf):
5305             schema = OctetString()
5306         seq = Seq(values)
5307         seq_encoded = seq.encode()
5308         seq_decoded, _ = seq.decode(seq_encoded)
5309         self.assertSequenceEqual(
5310             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
5311             b"".join(sorted([v.encode() for v in values])),
5312         )
5313
5314
5315 class TestGoMarshalVectors(TestCase):
5316     def runTest(self):
5317         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
5318         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
5319         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
5320         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
5321         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
5322
5323         class Seq(Sequence):
5324             schema = (
5325                 ("erste", Integer()),
5326                 ("zweite", Integer(optional=True))
5327             )
5328         seq = Seq()
5329         seq["erste"] = Integer(64)
5330         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5331         seq["erste"] = Integer(0x123456)
5332         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
5333         seq["erste"] = Integer(64)
5334         seq["zweite"] = Integer(65)
5335         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
5336
5337         class NestedSeq(Sequence):
5338             schema = (
5339                 ("nest", Seq()),
5340             )
5341         seq["erste"] = Integer(127)
5342         seq["zweite"] = None
5343         nested = NestedSeq()
5344         nested["nest"] = seq
5345         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
5346
5347         self.assertSequenceEqual(
5348             OctetString(b"\x01\x02\x03").encode(),
5349             hexdec("0403010203"),
5350         )
5351
5352         class Seq(Sequence):
5353             schema = (
5354                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
5355             )
5356         seq = Seq()
5357         seq["erste"] = Integer(64)
5358         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
5359
5360         class Seq(Sequence):
5361             schema = (
5362                 ("erste", Integer(expl=tag_ctxc(5))),
5363             )
5364         seq = Seq()
5365         seq["erste"] = Integer(64)
5366         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
5367
5368         class Seq(Sequence):
5369             schema = (
5370                 ("erste", Null(
5371                     impl=tag_encode(0, klass=TagClassContext),
5372                     optional=True,
5373                 )),
5374             )
5375         seq = Seq()
5376         seq["erste"] = Null()
5377         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
5378         seq["erste"] = None
5379         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5380
5381         self.assertSequenceEqual(
5382             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
5383             hexdec("170d3730303130313030303030305a"),
5384         )
5385         self.assertSequenceEqual(
5386             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
5387             hexdec("170d3039313131353232353631365a"),
5388         )
5389         self.assertSequenceEqual(
5390             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
5391             hexdec("180f32313030303430353132303130315a"),
5392         )
5393
5394         class Seq(Sequence):
5395             schema = (
5396                 ("erste", GeneralizedTime()),
5397             )
5398         seq = Seq()
5399         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
5400         self.assertSequenceEqual(
5401             seq.encode(),
5402             hexdec("3011180f32303039313131353232353631365a"),
5403         )
5404
5405         self.assertSequenceEqual(
5406             BitString((1, b"\x80")).encode(),
5407             hexdec("03020780"),
5408         )
5409         self.assertSequenceEqual(
5410             BitString((12, b"\x81\xF0")).encode(),
5411             hexdec("03030481f0"),
5412         )
5413
5414         self.assertSequenceEqual(
5415             ObjectIdentifier("1.2.3.4").encode(),
5416             hexdec("06032a0304"),
5417         )
5418         self.assertSequenceEqual(
5419             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
5420             hexdec("06092a864888932d010105"),
5421         )
5422         self.assertSequenceEqual(
5423             ObjectIdentifier("2.100.3").encode(),
5424             hexdec("0603813403"),
5425         )
5426
5427         self.assertSequenceEqual(
5428             PrintableString("test").encode(),
5429             hexdec("130474657374"),
5430         )
5431         self.assertSequenceEqual(
5432             PrintableString("x" * 127).encode(),
5433             hexdec("137F" + "78" * 127),
5434         )
5435         self.assertSequenceEqual(
5436             PrintableString("x" * 128).encode(),
5437             hexdec("138180" + "78" * 128),
5438         )
5439         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
5440
5441         class Seq(Sequence):
5442             schema = (
5443                 ("erste", IA5String()),
5444             )
5445         seq = Seq()
5446         seq["erste"] = IA5String("test")
5447         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
5448
5449         class Seq(Sequence):
5450             schema = (
5451                 ("erste", PrintableString()),
5452             )
5453         seq = Seq()
5454         seq["erste"] = PrintableString("test")
5455         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
5456         seq["erste"] = PrintableString("test*")
5457         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
5458
5459         class Seq(Sequence):
5460             schema = (
5461                 ("erste", Any(optional=True)),
5462                 ("zweite", Integer()),
5463             )
5464         seq = Seq()
5465         seq["zweite"] = Integer(64)
5466         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
5467
5468         class Seq(SetOf):
5469             schema = Integer()
5470         seq = Seq()
5471         seq.append(Integer(10))
5472         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
5473
5474         class _SeqOf(SequenceOf):
5475             schema = PrintableString()
5476
5477         class SeqOf(SequenceOf):
5478             schema = _SeqOf()
5479         _seqof = _SeqOf()
5480         _seqof.append(PrintableString("1"))
5481         seqof = SeqOf()
5482         seqof.append(_seqof)
5483         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
5484
5485         class Seq(Sequence):
5486             schema = (
5487                 ("erste", Integer(default=1)),
5488             )
5489         seq = Seq()
5490         seq["erste"] = Integer(0)
5491         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
5492         seq["erste"] = Integer(1)
5493         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
5494         seq["erste"] = Integer(2)
5495         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
5496
5497
5498 class TestPP(TestCase):
5499     @given(data_strategy())
5500     def test_oid_printing(self, d):
5501         oids = {
5502             str(ObjectIdentifier(k)): v * 2
5503             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
5504         }
5505         chosen = d.draw(sampled_from(sorted(oids)))
5506         chosen_id = oids[chosen]
5507         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
5508         self.assertNotIn(chosen_id, pp_console_row(pp))
5509         self.assertIn(chosen_id, pp_console_row(pp, oids=oids))
5510
5511
5512 class TestAutoAddSlots(TestCase):
5513     def runTest(self):
5514         class Inher(Integer):
5515             pass
5516
5517         with self.assertRaises(AttributeError):
5518             inher = Inher()
5519             inher.unexistent = "whatever"
5520
5521
5522 class TestOIDDefines(TestCase):
5523     @given(data_strategy())
5524     def runTest(self, d):
5525         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
5526         value_name_chosen = d.draw(sampled_from(value_names))
5527         oids = [
5528             ObjectIdentifier(oid)
5529             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
5530         ]
5531         oid_chosen = d.draw(sampled_from(oids))
5532         values = d.draw(lists(
5533             integers(),
5534             min_size=len(value_names),
5535             max_size=len(value_names),
5536         ))
5537         _schema = [
5538             ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
5539                 oid: Integer() for oid in oids[:-1]
5540             }),))),
5541         ]
5542         for i, value_name in enumerate(value_names):
5543             _schema.append((value_name, Any(expl=tag_ctxp(i))))
5544
5545         class Seq(Sequence):
5546             schema = _schema
5547         seq = Seq()
5548         for value_name, value in zip(value_names, values):
5549             seq[value_name] = Any(Integer(value).encode())
5550         seq["type"] = oid_chosen
5551         seq, _ = Seq().decode(seq.encode())
5552         for value_name in value_names:
5553             if value_name == value_name_chosen:
5554                 continue
5555             self.assertIsNone(seq[value_name].defined)
5556         if value_name_chosen in oids[:-1]:
5557             self.assertIsNotNone(seq[value_name_chosen].defined)
5558             self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
5559             self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
5560
5561
5562 class TestDefinesByPath(TestCase):
5563     def test_generated(self):
5564         class Seq(Sequence):
5565             schema = (
5566                 ("type", ObjectIdentifier()),
5567                 ("value", OctetString(expl=tag_ctxc(123))),
5568             )
5569
5570         class SeqInner(Sequence):
5571             schema = (
5572                 ("typeInner", ObjectIdentifier()),
5573                 ("valueInner", Any()),
5574             )
5575
5576         class PairValue(SetOf):
5577             schema = Any()
5578
5579         class Pair(Sequence):
5580             schema = (
5581                 ("type", ObjectIdentifier()),
5582                 ("value", PairValue()),
5583             )
5584
5585         class Pairs(SequenceOf):
5586             schema = Pair()
5587
5588         (
5589             type_integered,
5590             type_sequenced,
5591             type_innered,
5592             type_octet_stringed,
5593         ) = [
5594             ObjectIdentifier(oid)
5595             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
5596         ]
5597         seq_integered = Seq()
5598         seq_integered["type"] = type_integered
5599         seq_integered["value"] = OctetString(Integer(123).encode())
5600         seq_integered_raw = seq_integered.encode()
5601
5602         pairs = Pairs()
5603         pairs_input = (
5604             (type_octet_stringed, OctetString(b"whatever")),
5605             (type_integered, Integer(123)),
5606             (type_octet_stringed, OctetString(b"whenever")),
5607             (type_integered, Integer(234)),
5608         )
5609         for t, v in pairs_input:
5610             pair = Pair()
5611             pair["type"] = t
5612             pair["value"] = PairValue((Any(v),))
5613             pairs.append(pair)
5614         seq_inner = SeqInner()
5615         seq_inner["typeInner"] = type_innered
5616         seq_inner["valueInner"] = Any(pairs)
5617         seq_sequenced = Seq()
5618         seq_sequenced["type"] = type_sequenced
5619         seq_sequenced["value"] = OctetString(seq_inner.encode())
5620         seq_sequenced_raw = seq_sequenced.encode()
5621
5622         defines_by_path = []
5623         seq_integered, _ = Seq().decode(seq_integered_raw)
5624         self.assertIsNone(seq_integered["value"].defined)
5625         defines_by_path.append(
5626             (("type",), ((("value",), {
5627                 type_integered: Integer(),
5628                 type_sequenced: SeqInner(),
5629             }),))
5630         )
5631         seq_integered, _ = Seq().decode(
5632             seq_integered_raw,
5633             ctx={"defines_by_path": defines_by_path},
5634         )
5635         self.assertIsNotNone(seq_integered["value"].defined)
5636         self.assertEqual(seq_integered["value"].defined[0], type_integered)
5637         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
5638         self.assertTrue(seq_integered_raw[
5639             seq_integered["value"].defined[1].offset:
5640         ].startswith(Integer(123).encode()))
5641
5642         seq_sequenced, _ = Seq().decode(
5643             seq_sequenced_raw,
5644             ctx={"defines_by_path": defines_by_path},
5645         )
5646         self.assertIsNotNone(seq_sequenced["value"].defined)
5647         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5648         seq_inner = seq_sequenced["value"].defined[1]
5649         self.assertIsNone(seq_inner["valueInner"].defined)
5650
5651         defines_by_path.append((
5652             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
5653             ((("valueInner",), {type_innered: Pairs()}),),
5654         ))
5655         seq_sequenced, _ = Seq().decode(
5656             seq_sequenced_raw,
5657             ctx={"defines_by_path": defines_by_path},
5658         )
5659         self.assertIsNotNone(seq_sequenced["value"].defined)
5660         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5661         seq_inner = seq_sequenced["value"].defined[1]
5662         self.assertIsNotNone(seq_inner["valueInner"].defined)
5663         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5664         pairs = seq_inner["valueInner"].defined[1]
5665         for pair in pairs:
5666             self.assertIsNone(pair["value"][0].defined)
5667
5668         defines_by_path.append((
5669             (
5670                 "value",
5671                 DecodePathDefBy(type_sequenced),
5672                 "valueInner",
5673                 DecodePathDefBy(type_innered),
5674                 any,
5675                 "type",
5676             ),
5677             ((("value",), {
5678                 type_integered: Integer(),
5679                 type_octet_stringed: OctetString(),
5680             }),),
5681         ))
5682         seq_sequenced, _ = Seq().decode(
5683             seq_sequenced_raw,
5684             ctx={"defines_by_path": defines_by_path},
5685         )
5686         self.assertIsNotNone(seq_sequenced["value"].defined)
5687         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
5688         seq_inner = seq_sequenced["value"].defined[1]
5689         self.assertIsNotNone(seq_inner["valueInner"].defined)
5690         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
5691         pairs_got = seq_inner["valueInner"].defined[1]
5692         for pair_input, pair_got in zip(pairs_input, pairs_got):
5693             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
5694             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
5695
5696     @given(oid_strategy(), integers())
5697     def test_simple(self, oid, tgt):
5698         class Inner(Sequence):
5699             schema = (
5700                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
5701                     ObjectIdentifier(oid): Integer(),
5702                 }),))),
5703             )
5704
5705         class Outer(Sequence):
5706             schema = (
5707                 ("inner", Inner()),
5708                 ("tgt", OctetString()),
5709             )
5710
5711         inner = Inner()
5712         inner["oid"] = ObjectIdentifier(oid)
5713         outer = Outer()
5714         outer["inner"] = inner
5715         outer["tgt"] = OctetString(Integer(tgt).encode())
5716         decoded, _ = Outer().decode(outer.encode())
5717         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
5718
5719
5720 class TestAbsDecodePath(TestCase):
5721     @given(
5722         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5723         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5724     )
5725     def test_concat(self, decode_path, rel_path):
5726         self.assertSequenceEqual(
5727             abs_decode_path(decode_path, rel_path),
5728             decode_path + rel_path,
5729         )
5730
5731     @given(
5732         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
5733         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5734     )
5735     def test_abs(self, decode_path, rel_path):
5736         self.assertSequenceEqual(
5737             abs_decode_path(decode_path, ("/",) + rel_path),
5738             rel_path,
5739         )
5740
5741     @given(
5742         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
5743         integers(min_value=1, max_value=3),
5744         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
5745     )
5746     def test_dots(self, decode_path, number_of_dots, rel_path):
5747         self.assertSequenceEqual(
5748             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
5749             decode_path[:-number_of_dots] + rel_path,
5750         )
5751
5752
5753 class TestStrictDefaultExistence(TestCase):
5754     @given(data_strategy())
5755     def runTest(self, d):
5756         count = d.draw(integers(min_value=1, max_value=10))
5757         chosen = d.draw(integers(min_value=0, max_value=count - 1))
5758         _schema = [
5759             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
5760             for i in range(count)
5761         ]
5762
5763         class Seq(Sequence):
5764             schema = _schema
5765         seq = Seq()
5766         for i in range(count):
5767             seq["int%d" % i] = Integer(123)
5768         raw = seq.encode()
5769         chosen = "int%d" % chosen
5770         seq.specs[chosen] = seq.specs[chosen](default=123)
5771         seq.decode(raw)
5772         with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
5773             seq.decode(raw, ctx={"strict_default_existence": True})
5774
5775
5776 class TestX690PrefixedType(TestCase):
5777     def runTest(self):
5778         self.assertSequenceEqual(
5779             VisibleString("Jones").encode(),
5780             hexdec("1A054A6F6E6573"),
5781         )
5782         self.assertSequenceEqual(
5783             VisibleString(
5784                 "Jones",
5785                 impl=tag_encode(3, klass=TagClassApplication),
5786             ).encode(),
5787             hexdec("43054A6F6E6573"),
5788         )
5789         self.assertSequenceEqual(
5790             Any(
5791                 VisibleString(
5792                     "Jones",
5793                     impl=tag_encode(3, klass=TagClassApplication),
5794                 ),
5795                 expl=tag_ctxc(2),
5796             ).encode(),
5797             hexdec("A20743054A6F6E6573"),
5798         )
5799         self.assertSequenceEqual(
5800             OctetString(
5801                 VisibleString(
5802                     "Jones",
5803                     impl=tag_encode(3, klass=TagClassApplication),
5804                 ).encode(),
5805                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
5806             ).encode(),
5807             hexdec("670743054A6F6E6573"),
5808         )
5809         self.assertSequenceEqual(
5810             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
5811             hexdec("82054A6F6E6573"),
5812         )