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