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