]> Cypherpunks.ru repositories - pyderasn.git/blob - tests/test_pyderasn.py
Fix minimal tag value in tests: zero tag has only EOC
[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
6053 @composite
6054 def seqof_values_strategy(draw, schema=None, do_expl=False):
6055     if schema is None:
6056         schema = draw(sampled_from((Boolean(), Integer())))
6057     bound_min, bound_max = sorted(draw(sets(
6058         integers(min_value=0, max_value=10),
6059         min_size=2,
6060         max_size=2,
6061     )))
6062     if isinstance(schema, Boolean):
6063         values_generator = booleans().map(Boolean)
6064     elif isinstance(schema, Integer):
6065         values_generator = integers().map(Integer)
6066     values_generator = lists(
6067         values_generator,
6068         min_size=bound_min,
6069         max_size=bound_max,
6070     )
6071     values = draw(one_of(none(), values_generator))
6072     impl = None
6073     expl = None
6074     if do_expl:
6075         expl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6076     else:
6077         impl = draw(one_of(none(), integers(min_value=1).map(tag_encode)))
6078     default = draw(one_of(none(), values_generator))
6079     optional = draw(one_of(none(), booleans()))
6080     _decoded = (
6081         draw(integers(min_value=0)),
6082         draw(integers(min_value=0)),
6083         draw(integers(min_value=0)),
6084     )
6085     return (
6086         schema,
6087         values,
6088         (bound_min, bound_max),
6089         impl,
6090         expl,
6091         default,
6092         optional,
6093         _decoded,
6094     )
6095
6096
6097 class SeqOfMixing(object):
6098     def test_invalid_value_type(self):
6099         with self.assertRaises(InvalidValueType) as err:
6100             self.base_klass(123)
6101         repr(err.exception)
6102
6103     def test_invalid_values_type(self):
6104         class SeqOf(self.base_klass):
6105             schema = Integer()
6106         with self.assertRaises(InvalidValueType) as err:
6107             SeqOf([Integer(123), Boolean(False), Integer(234)])
6108         repr(err.exception)
6109
6110     def test_schema_required(self):
6111         with assertRaisesRegex(self, ValueError, "schema must be specified"):
6112             self.base_klass.__mro__[1]()
6113
6114     @given(booleans(), booleans(), binary(min_size=1), binary(min_size=1))
6115     def test_comparison(self, value1, value2, tag1, tag2):
6116         class SeqOf(self.base_klass):
6117             schema = Boolean()
6118         obj1 = SeqOf([Boolean(value1)])
6119         obj2 = SeqOf([Boolean(value2)])
6120         self.assertEqual(obj1 == obj2, value1 == value2)
6121         self.assertEqual(obj1 != obj2, value1 != value2)
6122         self.assertEqual(obj1 == list(obj2), value1 == value2)
6123         self.assertEqual(obj1 == tuple(obj2), value1 == value2)
6124         obj1 = SeqOf([Boolean(value1)], impl=tag1)
6125         obj2 = SeqOf([Boolean(value1)], impl=tag2)
6126         self.assertEqual(obj1 == obj2, tag1 == tag2)
6127         self.assertEqual(obj1 != obj2, tag1 != tag2)
6128
6129     @given(lists(booleans()))
6130     def test_iter(self, values):
6131         class SeqOf(self.base_klass):
6132             schema = Boolean()
6133         obj = SeqOf([Boolean(value) for value in values])
6134         self.assertEqual(len(obj), len(values))
6135         for i, value in enumerate(obj):
6136             self.assertEqual(value, values[i])
6137
6138     @given(data_strategy())
6139     def test_ready(self, d):
6140         ready = [Integer(v) for v in d.draw(lists(
6141             integers(),
6142             min_size=1,
6143             max_size=3,
6144         ))]
6145         non_ready = [
6146             Integer() for _ in
6147             range(d.draw(integers(min_value=1, max_value=5)))
6148         ]
6149
6150         class SeqOf(self.base_klass):
6151             schema = Integer()
6152         values = d.draw(permutations(ready + non_ready))
6153         seqof = SeqOf()
6154         for value in values:
6155             seqof.append(value)
6156         self.assertFalse(seqof.ready)
6157         repr(seqof)
6158         list(seqof.pps())
6159         pprint(seqof, big_blobs=True, with_decode_path=True)
6160         with self.assertRaises(ObjNotReady) as err:
6161             seqof.encode()
6162         repr(err.exception)
6163         for i, value in enumerate(values):
6164             self.assertEqual(seqof[i], value)
6165             if not seqof[i].ready:
6166                 seqof[i] = Integer(i)
6167         self.assertTrue(seqof.ready)
6168         repr(seqof)
6169         list(seqof.pps())
6170         pprint(seqof, big_blobs=True, with_decode_path=True)
6171
6172     def test_spec_mismatch(self):
6173         class SeqOf(self.base_klass):
6174             schema = Integer()
6175         seqof = SeqOf()
6176         seqof.append(Integer(123))
6177         with self.assertRaises(ValueError):
6178             seqof.append(Boolean(False))
6179         with self.assertRaises(ValueError):
6180             seqof[0] = Boolean(False)
6181
6182     @given(data_strategy())
6183     def test_bounds_satisfied(self, d):
6184         class SeqOf(self.base_klass):
6185             schema = Boolean()
6186         bound_min = d.draw(integers(min_value=0, max_value=1 << 7))
6187         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6188         value = [Boolean()] * d.draw(integers(min_value=bound_min, max_value=bound_max))
6189         SeqOf(value=value, bounds=(bound_min, bound_max))
6190
6191     @given(data_strategy())
6192     def test_bounds_unsatisfied(self, d):
6193         class SeqOf(self.base_klass):
6194             schema = Boolean()
6195         bound_min = d.draw(integers(min_value=1, max_value=1 << 7))
6196         bound_max = d.draw(integers(min_value=bound_min, max_value=1 << 7))
6197         value = [Boolean(False)] * d.draw(integers(max_value=bound_min - 1))
6198         with self.assertRaises(BoundsError) as err:
6199             SeqOf(value=value, bounds=(bound_min, bound_max))
6200         repr(err.exception)
6201         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6202             SeqOf(bounds=(bound_min, bound_max)).decode(
6203                 SeqOf(value).encode()
6204             )
6205         repr(err.exception)
6206         value = [Boolean(True)] * d.draw(integers(
6207             min_value=bound_max + 1,
6208             max_value=bound_max + 10,
6209         ))
6210         with self.assertRaises(BoundsError) as err:
6211             SeqOf(value=value, bounds=(bound_min, bound_max))
6212         repr(err.exception)
6213         with assertRaisesRegex(self, DecodeError, "bounds") as err:
6214             SeqOf(bounds=(bound_min, bound_max)).decode(
6215                 SeqOf(value).encode()
6216             )
6217         repr(err.exception)
6218
6219     @given(integers(min_value=1, max_value=10))
6220     def test_out_of_bounds(self, bound_max):
6221         class SeqOf(self.base_klass):
6222             schema = Integer()
6223             bounds = (0, bound_max)
6224         seqof = SeqOf()
6225         for _ in range(bound_max):
6226             seqof.append(Integer(123))
6227         with self.assertRaises(BoundsError):
6228             seqof.append(Integer(123))
6229
6230     @given(data_strategy())
6231     def test_call(self, d):
6232         (
6233             schema_initial,
6234             value_initial,
6235             bounds_initial,
6236             impl_initial,
6237             expl_initial,
6238             default_initial,
6239             optional_initial,
6240             _decoded_initial,
6241         ) = d.draw(seqof_values_strategy())
6242
6243         class SeqOf(self.base_klass):
6244             schema = schema_initial
6245         obj_initial = SeqOf(
6246             value=value_initial,
6247             bounds=bounds_initial,
6248             impl=impl_initial,
6249             expl=expl_initial,
6250             default=default_initial,
6251             optional=optional_initial or False,
6252             _decoded=_decoded_initial,
6253         )
6254         (
6255             _,
6256             value,
6257             bounds,
6258             impl,
6259             expl,
6260             default,
6261             optional,
6262             _decoded,
6263         ) = d.draw(seqof_values_strategy(
6264             schema=schema_initial,
6265             do_expl=impl_initial is None,
6266         ))
6267         if (default is None) and (obj_initial.default is not None):
6268             bounds = None
6269         if (
6270                 (bounds is None) and
6271                 (value is not None) and
6272                 (bounds_initial is not None) and
6273                 not (bounds_initial[0] <= len(value) <= bounds_initial[1])
6274         ):
6275             value = None
6276         if (
6277                 (bounds is None) and
6278                 (default is not None) and
6279                 (bounds_initial is not None) and
6280                 not (bounds_initial[0] <= len(default) <= bounds_initial[1])
6281         ):
6282             default = None
6283         obj = obj_initial(
6284             value=value,
6285             bounds=bounds,
6286             impl=impl,
6287             expl=expl,
6288             default=default,
6289             optional=optional,
6290         )
6291         if obj.ready:
6292             value_expected = default if value is None else value
6293             value_expected = (
6294                 default_initial if value_expected is None
6295                 else value_expected
6296             )
6297             value_expected = () if value_expected is None else value_expected
6298             self.assertEqual(obj, value_expected)
6299         self.assertEqual(obj.tag, impl or impl_initial or obj.tag_default)
6300         self.assertEqual(obj.expl_tag, expl or expl_initial)
6301         self.assertEqual(
6302             obj.default,
6303             default_initial if default is None else default,
6304         )
6305         if obj.default is None:
6306             optional = optional_initial if optional is None else optional
6307             optional = False if optional is None else optional
6308         else:
6309             optional = True
6310         self.assertEqual(obj.optional, optional)
6311         self.assertEqual(
6312             (obj._bound_min, obj._bound_max),
6313             bounds or bounds_initial or (0, float("+inf")),
6314         )
6315
6316     @given(seqof_values_strategy())
6317     def test_copy(self, values):
6318         _schema, value, bounds, impl, expl, default, optional, _decoded = values
6319
6320         class SeqOf(self.base_klass):
6321             schema = _schema
6322         register_class(SeqOf)
6323         obj = SeqOf(
6324             value=value,
6325             bounds=bounds,
6326             impl=impl,
6327             expl=expl,
6328             default=default,
6329             optional=optional or False,
6330             _decoded=_decoded,
6331         )
6332         for copy_func in copy_funcs:
6333             obj_copied = copy_func(obj)
6334             self.assert_copied_basic_fields(obj, obj_copied)
6335             self.assertEqual(obj._bound_min, obj_copied._bound_min)
6336             self.assertEqual(obj._bound_max, obj_copied._bound_max)
6337             self.assertEqual(obj._value, obj_copied._value)
6338
6339     @given(
6340         lists(binary()),
6341         integers(min_value=1).map(tag_encode),
6342     )
6343     def test_stripped(self, values, tag_impl):
6344         class SeqOf(self.base_klass):
6345             schema = OctetString()
6346         obj = SeqOf([OctetString(v) for v in values], impl=tag_impl)
6347         with self.assertRaises(NotEnoughData):
6348             obj.decode(obj.encode()[:-1])
6349
6350     @given(
6351         lists(binary()),
6352         integers(min_value=1).map(tag_ctxc),
6353     )
6354     def test_stripped_expl(self, values, tag_expl):
6355         class SeqOf(self.base_klass):
6356             schema = OctetString()
6357         obj = SeqOf([OctetString(v) for v in values], expl=tag_expl)
6358         with self.assertRaises(NotEnoughData):
6359             obj.decode(obj.encode()[:-1])
6360
6361     @given(
6362         integers(min_value=31),
6363         integers(min_value=0),
6364         decode_path_strat,
6365     )
6366     def test_bad_tag(self, tag, offset, decode_path):
6367         with self.assertRaises(DecodeError) as err:
6368             self.base_klass().decode(
6369                 tag_encode(tag)[:-1],
6370                 offset=offset,
6371                 decode_path=decode_path,
6372             )
6373         repr(err.exception)
6374         self.assertEqual(err.exception.offset, offset)
6375         self.assertEqual(err.exception.decode_path, decode_path)
6376
6377     @given(
6378         integers(min_value=128),
6379         integers(min_value=0),
6380         decode_path_strat,
6381     )
6382     def test_bad_len(self, l, offset, decode_path):
6383         with self.assertRaises(DecodeError) as err:
6384             self.base_klass().decode(
6385                 self.base_klass.tag_default + len_encode(l)[:-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(binary(min_size=1))
6394     def test_tag_mismatch(self, impl):
6395         assume(impl != self.base_klass.tag_default)
6396         with self.assertRaises(TagMismatch):
6397             self.base_klass(impl=impl).decode(self.base_klass().encode())
6398
6399     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6400     @given(
6401         seqof_values_strategy(schema=Integer()),
6402         lists(integers().map(Integer)),
6403         integers(min_value=1).map(tag_ctxc),
6404         integers(min_value=0),
6405         binary(max_size=5),
6406     )
6407     def test_symmetric(self, values, value, tag_expl, offset, tail_junk):
6408         _, _, _, _, _, default, optional, _decoded = values
6409
6410         class SeqOf(self.base_klass):
6411             schema = Integer()
6412         obj = SeqOf(
6413             value=value,
6414             default=default,
6415             optional=optional,
6416             _decoded=_decoded,
6417         )
6418         repr(obj)
6419         list(obj.pps())
6420         pprint(obj, big_blobs=True, with_decode_path=True)
6421         self.assertFalse(obj.expled)
6422         obj_encoded = obj.encode()
6423         obj_expled = obj(value, expl=tag_expl)
6424         self.assertTrue(obj_expled.expled)
6425         repr(obj_expled)
6426         list(obj_expled.pps())
6427         pprint(obj_expled, big_blobs=True, with_decode_path=True)
6428         obj_expled_encoded = obj_expled.encode()
6429         ctx_copied = deepcopy(ctx_dummy)
6430         obj_decoded, tail = obj_expled.decode(
6431             obj_expled_encoded + tail_junk,
6432             offset=offset,
6433             ctx=ctx_copied,
6434         )
6435         self.assertDictEqual(ctx_copied, ctx_dummy)
6436         repr(obj_decoded)
6437         list(obj_decoded.pps())
6438         pprint(obj_decoded, big_blobs=True, with_decode_path=True)
6439         self.assertEqual(tail, tail_junk)
6440         self._test_symmetric_compare_objs(obj_decoded, obj_expled)
6441         self.assertSequenceEqual(obj_decoded.encode(), obj_expled_encoded)
6442         self.assertSequenceEqual(obj_decoded.expl_tag, tag_expl)
6443         self.assertEqual(obj_decoded.expl_tlen, len(tag_expl))
6444         self.assertEqual(
6445             obj_decoded.expl_llen,
6446             len(len_encode(len(obj_encoded))),
6447         )
6448         self.assertEqual(obj_decoded.tlvlen, len(obj_encoded))
6449         self.assertEqual(obj_decoded.expl_vlen, len(obj_encoded))
6450         self.assertEqual(
6451             obj_decoded.offset,
6452             offset + obj_decoded.expl_tlen + obj_decoded.expl_llen,
6453         )
6454         self.assertEqual(obj_decoded.expl_offset, offset)
6455         for obj_inner in obj_decoded:
6456             self.assertIn(obj_inner, obj_decoded)
6457             self.assertSequenceEqual(
6458                 obj_inner.encode(),
6459                 obj_expled_encoded[
6460                     obj_inner.offset - offset:
6461                     obj_inner.offset + obj_inner.tlvlen - offset
6462                 ],
6463             )
6464
6465         t, _, lv = tag_strip(obj_encoded)
6466         _, _, v = len_decode(lv)
6467         obj_encoded_lenindef = t + LENINDEF + v + EOC
6468         with self.assertRaises(DecodeError):
6469             obj.decode(obj_encoded_lenindef)
6470         obj_decoded_lenindef, tail_lenindef = obj.decode(
6471             obj_encoded_lenindef + tail_junk,
6472             ctx={"bered": True},
6473         )
6474         self.assertTrue(obj_decoded_lenindef.lenindef)
6475         self.assertTrue(obj_decoded_lenindef.bered)
6476         obj_decoded_lenindef = copy(obj_decoded_lenindef)
6477         self.assertTrue(obj_decoded_lenindef.lenindef)
6478         self.assertTrue(obj_decoded_lenindef.bered)
6479         repr(obj_decoded_lenindef)
6480         list(obj_decoded_lenindef.pps())
6481         pprint(obj_decoded_lenindef, big_blobs=True, with_decode_path=True)
6482         self.assertEqual(tail_lenindef, tail_junk)
6483         self.assertEqual(obj_decoded_lenindef.tlvlen, len(obj_encoded_lenindef))
6484         with self.assertRaises(DecodeError):
6485             obj.decode(obj_encoded_lenindef[:-1], ctx={"bered": True})
6486         with self.assertRaises(DecodeError):
6487             obj.decode(obj_encoded_lenindef[:-2], ctx={"bered": True})
6488
6489         assert_exceeding_data(
6490             self,
6491             lambda: obj_expled.decod(obj_expled_encoded + tail_junk),
6492             tail_junk,
6493         )
6494
6495     def test_bered(self):
6496         class SeqOf(self.base_klass):
6497             schema = Boolean()
6498         encoded = Boolean(False).encode()
6499         encoded += Boolean.tag_default + len_encode(1) + b"\x01"
6500         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
6501         with self.assertRaises(DecodeError):
6502             SeqOf().decode(encoded)
6503         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
6504         self.assertFalse(decoded.ber_encoded)
6505         self.assertFalse(decoded.lenindef)
6506         self.assertTrue(decoded.bered)
6507         decoded = copy(decoded)
6508         self.assertFalse(decoded.ber_encoded)
6509         self.assertFalse(decoded.lenindef)
6510         self.assertTrue(decoded.bered)
6511
6512         class SeqOf(self.base_klass):
6513             schema = OctetString()
6514         encoded = OctetString(b"whatever").encode()
6515         encoded += (
6516             tag_encode(form=TagFormConstructed, num=4) +
6517             LENINDEF +
6518             OctetString(b"whatever").encode() +
6519             EOC
6520         )
6521         encoded = SeqOf.tag_default + len_encode(len(encoded)) + encoded
6522         with self.assertRaises(DecodeError):
6523             SeqOf().decode(encoded)
6524         decoded, _ = SeqOf().decode(encoded, ctx={"bered": True})
6525         self.assertFalse(decoded.ber_encoded)
6526         self.assertFalse(decoded.lenindef)
6527         self.assertTrue(decoded.bered)
6528         decoded = copy(decoded)
6529         self.assertFalse(decoded.ber_encoded)
6530         self.assertFalse(decoded.lenindef)
6531         self.assertTrue(decoded.bered)
6532
6533
6534 class TestSequenceOf(SeqOfMixing, CommonMixin, TestCase):
6535     class SeqOf(SequenceOf):
6536         schema = "whatever"
6537     base_klass = SeqOf
6538
6539     def _test_symmetric_compare_objs(self, obj1, obj2):
6540         self.assertEqual(obj1, obj2)
6541         self.assertSequenceEqual(list(obj1), list(obj2))
6542
6543
6544 class TestSetOf(SeqOfMixing, CommonMixin, TestCase):
6545     class SeqOf(SetOf):
6546         schema = "whatever"
6547     base_klass = SeqOf
6548
6549     def _test_symmetric_compare_objs(self, obj1, obj2):
6550         self.assertSetEqual(
6551             set(int(v) for v in obj1),
6552             set(int(v) for v in obj2),
6553         )
6554
6555     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6556     @given(data_strategy())
6557     def test_sorted(self, d):
6558         values = [OctetString(v) for v in d.draw(lists(binary()))]
6559
6560         class Seq(SetOf):
6561             schema = OctetString()
6562         seq = Seq(values)
6563         seq_encoded = seq.encode()
6564         seq_decoded, _ = seq.decode(seq_encoded)
6565         self.assertSequenceEqual(
6566             seq_encoded[seq_decoded.tlen + seq_decoded.llen:],
6567             b"".join(sorted([v.encode() for v in values])),
6568         )
6569
6570     @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
6571     @given(data_strategy())
6572     def test_unsorted(self, d):
6573         values = [OctetString(v).encode() for v in d.draw(sets(
6574             binary(min_size=1, max_size=5),
6575             min_size=2,
6576             max_size=5,
6577         ))]
6578         values = d.draw(permutations(values))
6579         assume(values != sorted(values))
6580         encoded = b"".join(values)
6581         seq_encoded = b"".join((
6582             SetOf.tag_default,
6583             len_encode(len(encoded)),
6584             encoded,
6585         ))
6586
6587         class Seq(SetOf):
6588             schema = OctetString()
6589         seq = Seq()
6590         with assertRaisesRegex(self, DecodeError, "unordered SET OF"):
6591             seq.decode(seq_encoded)
6592
6593         for ctx in ({"bered": True}, {"allow_unordered_set": True}):
6594             seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
6595             self.assertTrue(seq_decoded.ber_encoded)
6596             self.assertTrue(seq_decoded.bered)
6597             seq_decoded = copy(seq_decoded)
6598             self.assertTrue(seq_decoded.ber_encoded)
6599             self.assertTrue(seq_decoded.bered)
6600             self.assertSequenceEqual(
6601                 [obj.encode() for obj in seq_decoded],
6602                 values,
6603             )
6604
6605
6606 class TestGoMarshalVectors(TestCase):
6607     def runTest(self):
6608         self.assertSequenceEqual(Integer(10).encode(), hexdec("02010a"))
6609         self.assertSequenceEqual(Integer(127).encode(), hexdec("02017f"))
6610         self.assertSequenceEqual(Integer(128).encode(), hexdec("02020080"))
6611         self.assertSequenceEqual(Integer(-128).encode(), hexdec("020180"))
6612         self.assertSequenceEqual(Integer(-129).encode(), hexdec("0202ff7f"))
6613
6614         class Seq(Sequence):
6615             schema = (
6616                 ("erste", Integer()),
6617                 ("zweite", Integer(optional=True))
6618             )
6619         seq = Seq()
6620         seq["erste"] = Integer(64)
6621         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6622         seq["erste"] = Integer(0x123456)
6623         self.assertSequenceEqual(seq.encode(), hexdec("30050203123456"))
6624         seq["erste"] = Integer(64)
6625         seq["zweite"] = Integer(65)
6626         self.assertSequenceEqual(seq.encode(), hexdec("3006020140020141"))
6627
6628         class NestedSeq(Sequence):
6629             schema = (
6630                 ("nest", Seq()),
6631             )
6632         seq["erste"] = Integer(127)
6633         seq["zweite"] = None
6634         nested = NestedSeq()
6635         nested["nest"] = seq
6636         self.assertSequenceEqual(nested.encode(), hexdec("3005300302017f"))
6637
6638         self.assertSequenceEqual(
6639             OctetString(b"\x01\x02\x03").encode(),
6640             hexdec("0403010203"),
6641         )
6642
6643         class Seq(Sequence):
6644             schema = (
6645                 ("erste", Integer(impl=tag_encode(5, klass=TagClassContext))),
6646             )
6647         seq = Seq()
6648         seq["erste"] = Integer(64)
6649         self.assertSequenceEqual(seq.encode(), hexdec("3003850140"))
6650
6651         class Seq(Sequence):
6652             schema = (
6653                 ("erste", Integer(expl=tag_ctxc(5))),
6654             )
6655         seq = Seq()
6656         seq["erste"] = Integer(64)
6657         self.assertSequenceEqual(seq.encode(), hexdec("3005a503020140"))
6658
6659         class Seq(Sequence):
6660             schema = (
6661                 ("erste", Null(
6662                     impl=tag_encode(0, klass=TagClassContext),
6663                     optional=True,
6664                 )),
6665             )
6666         seq = Seq()
6667         seq["erste"] = Null()
6668         self.assertSequenceEqual(seq.encode(), hexdec("30028000"))
6669         seq["erste"] = None
6670         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6671
6672         self.assertSequenceEqual(
6673             UTCTime(datetime(1970, 1, 1, 0, 0)).encode(),
6674             hexdec("170d3730303130313030303030305a"),
6675         )
6676         self.assertSequenceEqual(
6677             UTCTime(datetime(2009, 11, 15, 22, 56, 16)).encode(),
6678             hexdec("170d3039313131353232353631365a"),
6679         )
6680         self.assertSequenceEqual(
6681             GeneralizedTime(datetime(2100, 4, 5, 12, 1, 1)).encode(),
6682             hexdec("180f32313030303430353132303130315a"),
6683         )
6684
6685         class Seq(Sequence):
6686             schema = (
6687                 ("erste", GeneralizedTime()),
6688             )
6689         seq = Seq()
6690         seq["erste"] = GeneralizedTime(datetime(2009, 11, 15, 22, 56, 16))
6691         self.assertSequenceEqual(
6692             seq.encode(),
6693             hexdec("3011180f32303039313131353232353631365a"),
6694         )
6695
6696         self.assertSequenceEqual(
6697             BitString((1, b"\x80")).encode(),
6698             hexdec("03020780"),
6699         )
6700         self.assertSequenceEqual(
6701             BitString((12, b"\x81\xF0")).encode(),
6702             hexdec("03030481f0"),
6703         )
6704
6705         self.assertSequenceEqual(
6706             ObjectIdentifier("1.2.3.4").encode(),
6707             hexdec("06032a0304"),
6708         )
6709         self.assertSequenceEqual(
6710             ObjectIdentifier("1.2.840.133549.1.1.5").encode(),
6711             hexdec("06092a864888932d010105"),
6712         )
6713         self.assertSequenceEqual(
6714             ObjectIdentifier("2.100.3").encode(),
6715             hexdec("0603813403"),
6716         )
6717
6718         self.assertSequenceEqual(
6719             PrintableString("test").encode(),
6720             hexdec("130474657374"),
6721         )
6722         self.assertSequenceEqual(
6723             PrintableString("x" * 127).encode(),
6724             hexdec("137F" + "78" * 127),
6725         )
6726         self.assertSequenceEqual(
6727             PrintableString("x" * 128).encode(),
6728             hexdec("138180" + "78" * 128),
6729         )
6730         self.assertSequenceEqual(UTF8String("Σ").encode(), hexdec("0c02cea3"))
6731
6732         class Seq(Sequence):
6733             schema = (
6734                 ("erste", IA5String()),
6735             )
6736         seq = Seq()
6737         seq["erste"] = IA5String("test")
6738         self.assertSequenceEqual(seq.encode(), hexdec("3006160474657374"))
6739
6740         class Seq(Sequence):
6741             schema = (
6742                 ("erste", PrintableString()),
6743             )
6744         seq = Seq()
6745         seq["erste"] = PrintableString("test")
6746         self.assertSequenceEqual(seq.encode(), hexdec("3006130474657374"))
6747         # Asterisk is actually not allowable
6748         PrintableString._allowable_chars |= set(b"*")
6749         seq["erste"] = PrintableString("test*")
6750         self.assertSequenceEqual(seq.encode(), hexdec("30071305746573742a"))
6751         PrintableString._allowable_chars -= set(b"*")
6752
6753         class Seq(Sequence):
6754             schema = (
6755                 ("erste", Any(optional=True)),
6756                 ("zweite", Integer()),
6757             )
6758         seq = Seq()
6759         seq["zweite"] = Integer(64)
6760         self.assertSequenceEqual(seq.encode(), hexdec("3003020140"))
6761
6762         class Seq(SetOf):
6763             schema = Integer()
6764         seq = Seq()
6765         seq.append(Integer(10))
6766         self.assertSequenceEqual(seq.encode(), hexdec("310302010a"))
6767
6768         class _SeqOf(SequenceOf):
6769             schema = PrintableString()
6770
6771         class SeqOf(SequenceOf):
6772             schema = _SeqOf()
6773         _seqof = _SeqOf()
6774         _seqof.append(PrintableString("1"))
6775         seqof = SeqOf()
6776         seqof.append(_seqof)
6777         self.assertSequenceEqual(seqof.encode(), hexdec("30053003130131"))
6778
6779         class Seq(Sequence):
6780             schema = (
6781                 ("erste", Integer(default=1)),
6782             )
6783         seq = Seq()
6784         seq["erste"] = Integer(0)
6785         self.assertSequenceEqual(seq.encode(), hexdec("3003020100"))
6786         seq["erste"] = Integer(1)
6787         self.assertSequenceEqual(seq.encode(), hexdec("3000"))
6788         seq["erste"] = Integer(2)
6789         self.assertSequenceEqual(seq.encode(), hexdec("3003020102"))
6790
6791
6792 class TestPP(TestCase):
6793     @given(data_strategy())
6794     def test_oid_printing(self, d):
6795         oids = {
6796             str(ObjectIdentifier(k)): v * 2
6797             for k, v in d.draw(dictionaries(oid_strategy(), text_letters())).items()
6798         }
6799         chosen = d.draw(sampled_from(sorted(oids)))
6800         chosen_id = oids[chosen]
6801         pp = _pp(asn1_type_name=ObjectIdentifier.asn1_type_name, value=chosen)
6802         self.assertNotIn(chosen_id, pp_console_row(pp))
6803         self.assertIn(
6804             chosen_id,
6805             pp_console_row(pp, oid_maps=[{'whatever': 'whenever'}, oids]),
6806         )
6807
6808
6809 class TestAutoAddSlots(TestCase):
6810     def runTest(self):
6811         class Inher(Integer):
6812             pass
6813
6814         with self.assertRaises(AttributeError):
6815             inher = Inher()
6816             inher.unexistent = "whatever"
6817
6818
6819 class TestOIDDefines(TestCase):
6820     @given(data_strategy())
6821     def runTest(self, d):
6822         value_names = list(d.draw(sets(text_letters(), min_size=1, max_size=10)))
6823         value_name_chosen = d.draw(sampled_from(value_names))
6824         oids = [
6825             ObjectIdentifier(oid)
6826             for oid in d.draw(sets(oid_strategy(), min_size=2, max_size=10))
6827         ]
6828         oid_chosen = d.draw(sampled_from(oids))
6829         values = d.draw(lists(
6830             integers(),
6831             min_size=len(value_names),
6832             max_size=len(value_names),
6833         ))
6834         for definable_class in (Any, OctetString, BitString):
6835             _schema = [
6836                 ("type", ObjectIdentifier(defines=(((value_name_chosen,), {
6837                     oid: Integer() for oid in oids[:-1]
6838                 }),))),
6839             ]
6840             for i, value_name in enumerate(value_names):
6841                 _schema.append((value_name, definable_class(expl=tag_ctxp(i))))
6842
6843             class Seq(Sequence):
6844                 schema = _schema
6845             seq = Seq()
6846             for value_name, value in zip(value_names, values):
6847                 seq[value_name] = definable_class(Integer(value).encode())
6848             seq["type"] = oid_chosen
6849             seq, _ = Seq().decode(seq.encode())
6850             for value_name in value_names:
6851                 if value_name == value_name_chosen:
6852                     continue
6853                 self.assertIsNone(seq[value_name].defined)
6854             if value_name_chosen in oids[:-1]:
6855                 self.assertIsNotNone(seq[value_name_chosen].defined)
6856                 self.assertEqual(seq[value_name_chosen].defined[0], oid_chosen)
6857                 self.assertIsInstance(seq[value_name_chosen].defined[1], Integer)
6858             repr(seq)
6859             list(seq.pps())
6860             pprint(seq, big_blobs=True, with_decode_path=True)
6861
6862
6863 class TestDefinesByPath(TestCase):
6864     def test_generated(self):
6865         class Seq(Sequence):
6866             schema = (
6867                 ("type", ObjectIdentifier()),
6868                 ("value", OctetString(expl=tag_ctxc(123))),
6869             )
6870
6871         class SeqInner(Sequence):
6872             schema = (
6873                 ("typeInner", ObjectIdentifier()),
6874                 ("valueInner", Any()),
6875             )
6876
6877         class PairValue(SetOf):
6878             schema = Any()
6879
6880         class Pair(Sequence):
6881             schema = (
6882                 ("type", ObjectIdentifier()),
6883                 ("value", PairValue()),
6884             )
6885
6886         class Pairs(SequenceOf):
6887             schema = Pair()
6888
6889         (
6890             type_integered,
6891             type_sequenced,
6892             type_innered,
6893             type_octet_stringed,
6894         ) = [
6895             ObjectIdentifier(oid)
6896             for oid in sets(oid_strategy(), min_size=4, max_size=4).example()
6897         ]
6898         seq_integered = Seq()
6899         seq_integered["type"] = type_integered
6900         seq_integered["value"] = OctetString(Integer(123).encode())
6901         seq_integered_raw = seq_integered.encode()
6902
6903         pairs = Pairs()
6904         pairs_input = (
6905             (type_octet_stringed, OctetString(b"whatever")),
6906             (type_integered, Integer(123)),
6907             (type_octet_stringed, OctetString(b"whenever")),
6908             (type_integered, Integer(234)),
6909         )
6910         for t, v in pairs_input:
6911             pairs.append(Pair((
6912                 ("type", t),
6913                 ("value", PairValue((Any(v),))),
6914             )))
6915         seq_inner = SeqInner()
6916         seq_inner["typeInner"] = type_innered
6917         seq_inner["valueInner"] = Any(pairs)
6918         seq_sequenced = Seq()
6919         seq_sequenced["type"] = type_sequenced
6920         seq_sequenced["value"] = OctetString(seq_inner.encode())
6921         seq_sequenced_raw = seq_sequenced.encode()
6922         repr(seq_sequenced)
6923         list(seq_sequenced.pps())
6924         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6925
6926         defines_by_path = []
6927         ctx_copied = deepcopy(ctx_dummy)
6928         seq_integered, _ = Seq().decode(
6929             seq_integered_raw,
6930             ctx=ctx_copied,
6931         )
6932         self.assertDictEqual(ctx_copied, ctx_dummy)
6933         self.assertIsNone(seq_integered["value"].defined)
6934         defines_by_path.append(
6935             (("type",), ((("value",), {
6936                 type_integered: Integer(),
6937                 type_sequenced: SeqInner(),
6938             }),))
6939         )
6940         ctx_copied["defines_by_path"] = defines_by_path
6941         seq_integered, _ = Seq().decode(
6942             seq_integered_raw,
6943             ctx=ctx_copied,
6944         )
6945         del ctx_copied["defines_by_path"]
6946         self.assertDictEqual(ctx_copied, ctx_dummy)
6947         self.assertIsNotNone(seq_integered["value"].defined)
6948         self.assertEqual(seq_integered["value"].defined[0], type_integered)
6949         self.assertEqual(seq_integered["value"].defined[1], Integer(123))
6950         self.assertTrue(seq_integered_raw[
6951             seq_integered["value"].defined[1].offset:
6952         ].startswith(Integer(123).encode()))
6953         repr(seq_integered)
6954         list(seq_integered.pps())
6955         pprint(seq_integered, big_blobs=True, with_decode_path=True)
6956
6957         ctx_copied["defines_by_path"] = defines_by_path
6958         seq_sequenced, _ = Seq().decode(
6959             seq_sequenced_raw,
6960             ctx=ctx_copied,
6961         )
6962         del ctx_copied["defines_by_path"]
6963         self.assertDictEqual(ctx_copied, ctx_dummy)
6964         self.assertIsNotNone(seq_sequenced["value"].defined)
6965         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6966         seq_inner = seq_sequenced["value"].defined[1]
6967         self.assertIsNone(seq_inner["valueInner"].defined)
6968         repr(seq_sequenced)
6969         list(seq_sequenced.pps())
6970         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6971
6972         defines_by_path.append((
6973             ("value", DecodePathDefBy(type_sequenced), "typeInner"),
6974             ((("valueInner",), {type_innered: Pairs()}),),
6975         ))
6976         ctx_copied["defines_by_path"] = defines_by_path
6977         seq_sequenced, _ = Seq().decode(
6978             seq_sequenced_raw,
6979             ctx=ctx_copied,
6980         )
6981         del ctx_copied["defines_by_path"]
6982         self.assertDictEqual(ctx_copied, ctx_dummy)
6983         self.assertIsNotNone(seq_sequenced["value"].defined)
6984         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
6985         seq_inner = seq_sequenced["value"].defined[1]
6986         self.assertIsNotNone(seq_inner["valueInner"].defined)
6987         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
6988         pairs = seq_inner["valueInner"].defined[1]
6989         for pair in pairs:
6990             self.assertIsNone(pair["value"][0].defined)
6991         repr(seq_sequenced)
6992         list(seq_sequenced.pps())
6993         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
6994
6995         defines_by_path.append((
6996             (
6997                 "value",
6998                 DecodePathDefBy(type_sequenced),
6999                 "valueInner",
7000                 DecodePathDefBy(type_innered),
7001                 any,
7002                 "type",
7003             ),
7004             ((("value",), {
7005                 type_integered: Integer(),
7006                 type_octet_stringed: OctetString(),
7007             }),),
7008         ))
7009         ctx_copied["defines_by_path"] = defines_by_path
7010         seq_sequenced, _ = Seq().decode(
7011             seq_sequenced_raw,
7012             ctx=ctx_copied,
7013         )
7014         del ctx_copied["defines_by_path"]
7015         self.assertDictEqual(ctx_copied, ctx_dummy)
7016         self.assertIsNotNone(seq_sequenced["value"].defined)
7017         self.assertEqual(seq_sequenced["value"].defined[0], type_sequenced)
7018         seq_inner = seq_sequenced["value"].defined[1]
7019         self.assertIsNotNone(seq_inner["valueInner"].defined)
7020         self.assertEqual(seq_inner["valueInner"].defined[0], type_innered)
7021         pairs_got = seq_inner["valueInner"].defined[1]
7022         for pair_input, pair_got in zip(pairs_input, pairs_got):
7023             self.assertEqual(pair_got["value"][0].defined[0], pair_input[0])
7024             self.assertEqual(pair_got["value"][0].defined[1], pair_input[1])
7025         repr(seq_sequenced)
7026         list(seq_sequenced.pps())
7027         pprint(seq_sequenced, big_blobs=True, with_decode_path=True)
7028
7029     @given(oid_strategy(), integers())
7030     def test_simple(self, oid, tgt):
7031         class Inner(Sequence):
7032             schema = (
7033                 ("oid", ObjectIdentifier(defines=((("..", "tgt"), {
7034                     ObjectIdentifier(oid): Integer(),
7035                 }),))),
7036             )
7037
7038         class Outer(Sequence):
7039             schema = (
7040                 ("inner", Inner()),
7041                 ("tgt", OctetString()),
7042             )
7043
7044         inner = Inner()
7045         inner["oid"] = ObjectIdentifier(oid)
7046         outer = Outer()
7047         outer["inner"] = inner
7048         outer["tgt"] = OctetString(Integer(tgt).encode())
7049         decoded, _ = Outer().decode(outer.encode())
7050         self.assertEqual(decoded["tgt"].defined[1], Integer(tgt))
7051
7052     def test_remaining_data(self):
7053         oid = ObjectIdentifier("1.2.3")
7054         class Seq(Sequence):
7055             schema = (
7056                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7057                     oid: Integer(),
7058                 }),))),
7059                 ("tgt", OctetString()),
7060             )
7061
7062         seq = Seq((
7063             ("oid", oid),
7064             ("tgt", OctetString(Integer(123).encode() + b"junk")),
7065         ))
7066         with assertRaisesRegex(self, DecodeError, "remaining data"):
7067             Seq().decode(seq.encode())
7068
7069     def test_remaining_data_seqof(self):
7070         oid = ObjectIdentifier("1.2.3")
7071         class SeqOf(SetOf):
7072             schema = OctetString()
7073
7074         class Seq(Sequence):
7075             schema = (
7076                 ("oid", ObjectIdentifier(defines=((("tgt",), {
7077                     oid: Integer(),
7078                 }),))),
7079                 ("tgt", SeqOf()),
7080             )
7081
7082         seq = Seq((
7083             ("oid", oid),
7084             ("tgt", SeqOf([OctetString(Integer(123).encode() + b"junk")])),
7085         ))
7086         with assertRaisesRegex(self, DecodeError, "remaining data"):
7087             Seq().decode(seq.encode())
7088
7089
7090 class TestAbsDecodePath(TestCase):
7091     @given(
7092         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7093         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7094     )
7095     def test_concat(self, decode_path, rel_path):
7096         dp = abs_decode_path(decode_path, rel_path)
7097         self.assertSequenceEqual(dp, decode_path + rel_path)
7098         repr(dp)
7099
7100     @given(
7101         lists(text(alphabet=ascii_letters, min_size=1)).map(tuple),
7102         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7103     )
7104     def test_abs(self, decode_path, rel_path):
7105         self.assertSequenceEqual(
7106             abs_decode_path(decode_path, ("/",) + rel_path),
7107             rel_path,
7108         )
7109
7110     @given(
7111         lists(text(alphabet=ascii_letters, min_size=1), min_size=5).map(tuple),
7112         integers(min_value=1, max_value=3),
7113         lists(text(alphabet=ascii_letters, min_size=1), min_size=1).map(tuple),
7114     )
7115     def test_dots(self, decode_path, number_of_dots, rel_path):
7116         self.assertSequenceEqual(
7117             abs_decode_path(decode_path, tuple([".."] * number_of_dots) + rel_path),
7118             decode_path[:-number_of_dots] + rel_path,
7119         )
7120
7121
7122 class TestStrictDefaultExistence(TestCase):
7123     @given(data_strategy())
7124     def runTest(self, d):
7125         count = d.draw(integers(min_value=1, max_value=10))
7126         chosen = d.draw(integers(min_value=0, max_value=count - 1))
7127         _schema = [
7128             ("int%d" % i, Integer(expl=tag_ctxc(i + 1)))
7129             for i in range(count)
7130         ]
7131         for klass in (Sequence, Set):
7132             class Seq(klass):
7133                 schema = _schema
7134             seq = Seq()
7135             for i in range(count):
7136                 seq["int%d" % i] = Integer(123)
7137             raw = seq.encode()
7138             chosen_choice = "int%d" % chosen
7139             seq.specs[chosen_choice] = seq.specs[chosen_choice](default=123)
7140             with assertRaisesRegex(self, DecodeError, "DEFAULT value met"):
7141                 seq.decode(raw)
7142             decoded, _ = seq.decode(raw, ctx={"allow_default_values": True})
7143             self.assertTrue(decoded.ber_encoded)
7144             self.assertTrue(decoded.bered)
7145             decoded = copy(decoded)
7146             self.assertTrue(decoded.ber_encoded)
7147             self.assertTrue(decoded.bered)
7148             decoded, _ = seq.decode(raw, ctx={"bered": True})
7149             self.assertTrue(decoded.ber_encoded)
7150             self.assertTrue(decoded.bered)
7151             decoded = copy(decoded)
7152             self.assertTrue(decoded.ber_encoded)
7153             self.assertTrue(decoded.bered)
7154
7155
7156 class TestX690PrefixedType(TestCase):
7157     def runTest(self):
7158         self.assertSequenceEqual(
7159             VisibleString("Jones").encode(),
7160             hexdec("1A054A6F6E6573"),
7161         )
7162         self.assertSequenceEqual(
7163             VisibleString(
7164                 "Jones",
7165                 impl=tag_encode(3, klass=TagClassApplication),
7166             ).encode(),
7167             hexdec("43054A6F6E6573"),
7168         )
7169         self.assertSequenceEqual(
7170             Any(
7171                 VisibleString(
7172                     "Jones",
7173                     impl=tag_encode(3, klass=TagClassApplication),
7174                 ),
7175                 expl=tag_ctxc(2),
7176             ).encode(),
7177             hexdec("A20743054A6F6E6573"),
7178         )
7179         self.assertSequenceEqual(
7180             OctetString(
7181                 VisibleString(
7182                     "Jones",
7183                     impl=tag_encode(3, klass=TagClassApplication),
7184                 ).encode(),
7185                 impl=tag_encode(7, form=TagFormConstructed, klass=TagClassApplication),
7186             ).encode(),
7187             hexdec("670743054A6F6E6573"),
7188         )
7189         self.assertSequenceEqual(
7190             VisibleString("Jones", impl=tag_ctxp(2)).encode(),
7191             hexdec("82054A6F6E6573"),
7192         )
7193
7194
7195 class TestExplOOB(TestCase):
7196     def runTest(self):
7197         expl = tag_ctxc(123)
7198         raw = Integer(123).encode() + Integer(234).encode()
7199         raw = b"".join((expl, len_encode(len(raw)), raw))
7200         with assertRaisesRegex(self, DecodeError, "explicit tag out-of-bound"):
7201             Integer(expl=expl).decode(raw)
7202         Integer(expl=expl).decode(raw, ctx={"allow_expl_oob": True})
7203
7204
7205 class TestPickleDifferentVersion(TestCase):
7206     def runTest(self):
7207         pickled = pickle_dumps(Integer(123), pickle_proto)
7208         import pyderasn
7209         version_orig = pyderasn.__version__
7210         pyderasn.__version__ += "different"
7211         with assertRaisesRegex(self, ValueError, "different PyDERASN version"):
7212             pickle_loads(pickled)
7213         pyderasn.__version__ = version_orig
7214         pickle_loads(pickled)