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